diff options
465 files changed, 16121 insertions, 12399 deletions
diff --git a/Android.bp b/Android.bp index 78d38c5c2537..9c1a0855ec98 100644 --- a/Android.bp +++ b/Android.bp @@ -281,17 +281,33 @@ filegroup { filegroup { name: "framework-updatable-sources", srcs: [ + ":framework-mediaprovider-sources", + ":framework-permission-sources", ":framework-sdkextensions-sources", ":framework-statsd-sources", + ":framework-telephony-sources", ":framework-tethering-srcs", - ":updatable-media-srcs", - ":framework-mediaprovider-sources", - ":framework-permission-sources", ":framework-wifi-updatable-sources", - ":framework-telephony-sources", + ":updatable-media-srcs", ] } +java_library { + name: "framework-updatable-stubs-module_libs_api", + static_libs: [ + "framework-media-stubs-module_libs_api", + "framework-mediaprovider-stubs-module_libs_api", + "framework-permission-stubs-module_libs_api", + "framework-sdkextensions-stubs-module_libs_api", + "framework-statsd-stubs-module_libs_api", + "framework-telephony-stubs", // TODO: Update to module_libs_api when there is one. + "framework-tethering-stubs-module_libs_api", + "framework-wifi-stubs-module_libs_api", + ], + sdk_version: "module_current", + visibility: [":__pkg__"], +} + filegroup { name: "framework-all-sources", srcs: [ @@ -307,7 +323,6 @@ java_defaults { name: "framework-aidl-export-defaults", aidl: { export_include_dirs: [ - "apex/media/framework/java", "core/java", "drm/java", "graphics/java", @@ -324,6 +339,12 @@ java_defaults { "rs/java", "sax/java", "telecomm/java", + + // TODO(b/148660295): remove this + "apex/media/framework/java", + + // TODO(b/147699819): remove this + "telephony/java", ], }, } @@ -397,9 +418,7 @@ java_defaults { "app-compat-annotations", "ext", "unsupportedappusage", - "framework-media-stubs-systemapi", - "framework-mediaprovider-stubs-systemapi", - "framework-telephony-stubs", + "framework-updatable-stubs-module_libs_api", ], jarjar_rules: ":framework-jarjar-rules", @@ -465,13 +484,6 @@ java_library { name: "framework-minus-apex", defaults: ["framework-defaults"], srcs: [":framework-non-updatable-sources"], - libs: [ - "framework-sdkextensions-stubs-systemapi", - "framework-statsd-stubs-module_libs_api", - "framework-permission-stubs-systemapi", - "framework-wifi-stubs-systemapi", - "framework-tethering-stubs", - ], installable: true, javac_shard_size: 150, required: [ @@ -512,16 +524,9 @@ java_library { installable: false, // this lib is a build-only library static_libs: [ "framework-minus-apex", - "framework-media-stubs-systemapi", - "framework-mediaprovider-stubs-systemapi", - "framework-permission-stubs-systemapi", - "framework-sdkextensions-stubs-systemapi", - "framework-statsd-stubs-module_libs_api", - "framework-wifi-stubs-systemapi", - "framework-tethering-stubs", - // TODO (b/147688669) should be framework-telephony-stubs + // TODO (b/147688669) should be removed "framework-telephony", - // TODO(jiyong): add stubs for APEXes here + "framework-updatable-stubs-module_libs_api", ], sdk_version: "core_platform", apex_available: ["//apex_available:platform"], @@ -540,7 +545,6 @@ java_library { visibility: [ // DO NOT ADD ANY MORE ENTRIES TO THIS LIST "//external/robolectric-shadows:__subpackages__", - "//frameworks/base/packages/Tethering/common/TetheringLib:__subpackages__", "//frameworks/layoutlib:__subpackages__", "//frameworks/opt/net/ike:__subpackages__", ], diff --git a/apex/Android.bp b/apex/Android.bp index 051986e758d3..151091137c9c 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -43,6 +43,7 @@ stubs_defaults { name: "framework-module-stubs-defaults-publicapi", args: mainline_stubs_args, installable: false, + sdk_version: "current", } stubs_defaults { @@ -50,6 +51,7 @@ stubs_defaults { args: mainline_stubs_args + priv_apps, srcs: [":framework-annotations"], installable: false, + sdk_version: "system_current", } // The defaults for module_libs comes in two parts - defaults for API checks @@ -62,6 +64,7 @@ stubs_defaults { args: mainline_stubs_args + module_libs, srcs: [":framework-annotations"], installable: false, + sdk_version: "module_current", } stubs_defaults { @@ -69,4 +72,5 @@ stubs_defaults { args: mainline_stubs_args + module_libs + priv_apps, srcs: [":framework-annotations"], installable: false, + sdk_version: "module_current", } diff --git a/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java b/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java new file mode 100644 index 000000000000..3725ad4a6c09 --- /dev/null +++ b/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.blob; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * Class to provide information about an accessor of a shared blob. + * + * @hide + */ +public final class AccessorInfo implements Parcelable { + private final String mPackageName; + private final long mExpiryTimeMs; + private final int mDescriptionResId; + private final CharSequence mDescription; + + public AccessorInfo(String packageName, long expiryTimeMs, + int descriptionResId, CharSequence description) { + mPackageName = packageName; + mExpiryTimeMs = expiryTimeMs; + mDescriptionResId = descriptionResId; + mDescription = description; + } + + private AccessorInfo(Parcel in) { + mPackageName = in.readString(); + mExpiryTimeMs = in.readLong(); + mDescriptionResId = in.readInt(); + mDescription = in.readCharSequence(); + } + + public String getPackageName() { + return mPackageName; + } + + public long getExpiryTimeMs() { + return mExpiryTimeMs; + } + + public int getDescriptionResId() { + return mDescriptionResId; + } + + public CharSequence getDescription() { + return mDescription; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mPackageName); + dest.writeLong(mExpiryTimeMs); + dest.writeInt(mDescriptionResId); + dest.writeCharSequence(mDescription); + } + + @Override + public String toString() { + return "AccessorInfo {" + + "package: " + mPackageName + "," + + "expiryMs: " + mExpiryTimeMs + "," + + "descriptionResId: " + mDescriptionResId + "," + + "description: " + mDescription + "," + + "}"; + } + + private String toShortString() { + return mPackageName; + } + + public static String toShortString(List<AccessorInfo> accessors) { + final StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0, size = accessors.size(); i < size; ++i) { + sb.append(accessors.get(i).toShortString()); + sb.append(","); + } + sb.append("]"); + return sb.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<AccessorInfo> CREATOR = new Creator<AccessorInfo>() { + @Override + @NonNull + public AccessorInfo createFromParcel(Parcel source) { + return new AccessorInfo(source); + } + + @Override + @NonNull + public AccessorInfo[] newArray(int size) { + return new AccessorInfo[size]; + } + }; +} diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl b/apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl new file mode 100644 index 000000000000..25497738f685 --- /dev/null +++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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. + */ +package android.app.blob; + +/** {@hide} */ +parcelable BlobInfo;
\ No newline at end of file diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java new file mode 100644 index 000000000000..9746dd023002 --- /dev/null +++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.blob; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Collections; +import java.util.List; + +/** + * Class to provide information about a shared blob. + * + * @hide + */ +public final class BlobInfo implements Parcelable { + private final long mId; + private final long mExpiryTimeMs; + private final CharSequence mLabel; + private final List<AccessorInfo> mAccessors; + + public BlobInfo(long id, long expiryTimeMs, CharSequence label, + List<AccessorInfo> accessors) { + mId = id; + mExpiryTimeMs = expiryTimeMs; + mLabel = label; + mAccessors = accessors; + } + + private BlobInfo(Parcel in) { + mId = in.readLong(); + mExpiryTimeMs = in.readLong(); + mLabel = in.readCharSequence(); + mAccessors = in.readArrayList(null /* classloader */); + } + + public long getId() { + return mId; + } + + public long getExpiryTimeMs() { + return mExpiryTimeMs; + } + + public CharSequence getLabel() { + return mLabel; + } + + public List<AccessorInfo> getAccessors() { + return Collections.unmodifiableList(mAccessors); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mId); + dest.writeLong(mExpiryTimeMs); + dest.writeCharSequence(mLabel); + dest.writeList(mAccessors); + } + + @Override + public String toString() { + return toShortString(); + } + + private String toShortString() { + return "BlobInfo {" + + "id: " + mId + "," + + "expiryMs: " + mExpiryTimeMs + "," + + "label: " + mLabel + "," + + "accessors: " + AccessorInfo.toShortString(mAccessors) + "," + + "}"; + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<BlobInfo> CREATOR = new Creator<BlobInfo>() { + @Override + @NonNull + public BlobInfo createFromParcel(Parcel source) { + return new BlobInfo(source); + } + + @Override + @NonNull + public BlobInfo[] newArray(int size) { + return new BlobInfo[size]; + } + }; +} diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java index d1e28e99b6d6..814ab6dbd7fd 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java @@ -22,16 +22,19 @@ import android.annotation.IdRes; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.Context; import android.os.ParcelFileDescriptor; import android.os.ParcelableException; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.UserHandle; import com.android.internal.util.function.pooled.PooledLambda; import java.io.Closeable; import java.io.IOException; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -145,9 +148,6 @@ public class BlobStoreManager { /** @hide */ public static final int INVALID_RES_ID = -1; - /** @hide */ - public static final String DESC_RES_TYPE_STRING = "string"; - private final Context mContext; private final IBlobStoreManager mService; @@ -215,7 +215,7 @@ public class BlobStoreManager { } /** - * Delete an existing session and any data that was written to that session so far. + * Abandons an existing session and deletes any data that was written to that session so far. * * @param sessionId a unique id obtained via {@link #createSession(BlobHandle)} that * represents a particular session. @@ -224,9 +224,9 @@ public class BlobStoreManager { * @throws SecurityException when the caller does not own the session, or * the session does not exist or is invalid. */ - public void deleteSession(@IntRange(from = 1) long sessionId) throws IOException { + public void abandonSession(@IntRange(from = 1) long sessionId) throws IOException { try { - mService.deleteSession(sessionId, mContext.getOpPackageName()); + mService.abandonSession(sessionId, mContext.getOpPackageName()); } catch (ParcelableException e) { e.maybeRethrow(IOException.class); throw new RuntimeException(e); @@ -454,13 +454,13 @@ public class BlobStoreManager { } /** - * Release all active leases to the blob represented by {@code blobHandle} which are + * Release any active lease to the blob represented by {@code blobHandle} which is * currently held by the caller. * * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to - * release the leases for. + * release the lease for. * - * @throws IOException when there is an I/O error while releasing the releases to the blob. + * @throws IOException when there is an I/O error while releasing the release to the blob. * @throws SecurityException when the blob represented by the {@code blobHandle} does not * exist or the caller does not have access to it. * @throws IllegalArgumentException when {@code blobHandle} is invalid. @@ -481,6 +481,7 @@ public class BlobStoreManager { * * @hide */ + @TestApi public void waitForIdle(long timeoutMillis) throws InterruptedException, TimeoutException { try { final CountDownLatch countDownLatch = new CountDownLatch(1); @@ -495,6 +496,31 @@ public class BlobStoreManager { } } + /** @hide */ + @NonNull + public List<BlobInfo> queryBlobsForUser(@NonNull UserHandle user) throws IOException { + try { + return mService.queryBlobsForUser(user.getIdentifier()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void deleteBlob(@NonNull BlobInfo blobInfo) throws IOException { + try { + mService.deleteBlob(blobInfo.getId()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Represents an ongoing session of a blob's contribution to the blob store managed by the * system. @@ -599,7 +625,7 @@ public class BlobStoreManager { /** * Close this session. It can be re-opened for writing/reading if it has not been - * abandoned (using {@link #abandon}) or closed (using {@link #commit}). + * abandoned (using {@link #abandon}) or committed (using {@link #commit}). * * @throws IOException when there is an I/O error while closing the session. * @throws SecurityException when the caller is not the owner of the session. diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl index a85a25c9c4ad..e78381359b41 100644 --- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl +++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl @@ -16,6 +16,7 @@ package android.app.blob; import android.app.blob.BlobHandle; +import android.app.blob.BlobInfo; import android.app.blob.IBlobStoreSession; import android.os.RemoteCallback; @@ -24,11 +25,14 @@ interface IBlobStoreManager { long createSession(in BlobHandle handle, in String packageName); IBlobStoreSession openSession(long sessionId, in String packageName); ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName); - void deleteSession(long sessionId, in String packageName); + void abandonSession(long sessionId, in String packageName); void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description, long leaseTimeoutMillis, in String packageName); void releaseLease(in BlobHandle handle, in String packageName); void waitForIdle(in RemoteCallback callback); + + List<BlobInfo> queryBlobsForUser(int userId); + void deleteBlob(long blobId); }
\ No newline at end of file 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 dab4797b313f..970766d2c8a6 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java @@ -15,7 +15,6 @@ */ package com.android.server.blob; -import static android.app.blob.BlobStoreManager.DESC_RES_TYPE_STRING; import static android.app.blob.XmlTags.ATTR_DESCRIPTION; import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME; import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME; @@ -30,16 +29,16 @@ import static android.app.blob.XmlTags.TAG_LEASEE; import static android.os.Process.INVALID_UID; import static android.system.OsConstants.O_RDONLY; -import static com.android.server.blob.BlobStoreConfig.LOGV; import static com.android.server.blob.BlobStoreConfig.TAG; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC; +import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId; +import static com.android.server.blob.BlobStoreUtils.getPackageResources; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.blob.BlobHandle; import android.content.Context; -import android.content.pm.PackageManager; import android.content.res.ResourceId; import android.content.res.Resources; import android.os.ParcelFileDescriptor; @@ -65,6 +64,7 @@ import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.util.Objects; +import java.util.function.Consumer; class BlobMetadata { private final Object mMetadataLock = new Object(); @@ -281,6 +281,10 @@ class BlobMetadata { return false; } + void forEachLeasee(Consumer<Leasee> consumer) { + mLeasees.forEach(consumer); + } + File getBlobFile() { if (mBlobFile == null) { mBlobFile = BlobStoreConfig.getBlobFile(mBlobId); @@ -506,16 +510,9 @@ class BlobMetadata { if (resources == null) { return null; } - try { - final int resId = resources.getIdentifier(descriptionResEntryName, - DESC_RES_TYPE_STRING, packageName); - return resId <= 0 ? null : resources.getString(resId); - } catch (Resources.NotFoundException e) { - if (LOGV) { - Slog.w(TAG, "Description resource not found", e); - } - return null; - } + final int resId = getDescriptionResourceId(resources, descriptionResEntryName, + packageName); + return resId == Resources.ID_NULL ? null : resources.getString(resId); } @Nullable @@ -546,19 +543,6 @@ class BlobMetadata { return desc == null ? "<none>" : desc; } - @Nullable - private static Resources getPackageResources(@NonNull Context context, - @NonNull String packageName, int userId) { - try { - return context.getPackageManager() - .getResourcesForApplicationAsUser(packageName, userId); - } catch (PackageManager.NameNotFoundException e) { - Slog.d(TAG, "Unknown package in user " + userId + ": " - + packageName, e); - return null; - } - } - void writeToXml(@NonNull XmlSerializer out) throws IOException { XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName); XmlUtils.writeIntAttribute(out, ATTR_UID, uid); 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 96f7b7aee165..53a97cefa59b 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -36,6 +36,8 @@ import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED; import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID; import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_VALID; import static com.android.server.blob.BlobStoreSession.stateToString; +import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId; +import static com.android.server.blob.BlobStoreUtils.getPackageResources; import android.annotation.CurrentTimeSecondsLong; import android.annotation.IdRes; @@ -43,7 +45,9 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.blob.AccessorInfo; import android.app.blob.BlobHandle; +import android.app.blob.BlobInfo; import android.app.blob.IBlobStoreManager; import android.app.blob.IBlobStoreSession; import android.content.BroadcastReceiver; @@ -100,6 +104,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.ArrayList; @@ -110,6 +115,7 @@ import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; +import java.util.function.Function; /** * Service responsible for maintaining and facilitating access to data blobs published by apps. @@ -343,7 +349,7 @@ public class BlobStoreManagerService extends SystemService { return session; } - private void deleteSessionInternal(long sessionId, + private void abandonSessionInternal(long sessionId, int callingUid, String callingPackage) { synchronized (mBlobsLock) { final BlobStoreSession session = openSessionInternal(sessionId, @@ -351,7 +357,7 @@ public class BlobStoreManagerService extends SystemService { session.open(); session.abandon(); if (LOGV) { - Slog.v(TAG, "Deleted session with id " + sessionId + Slog.v(TAG, "Abandoned session with id " + sessionId + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage); } writeBlobSessionsAsync(); @@ -434,6 +440,48 @@ public class BlobStoreManagerService extends SystemService { } } + private List<BlobInfo> queryBlobsForUserInternal(int userId) { + final ArrayList<BlobInfo> blobInfos = new ArrayList<>(); + synchronized (mBlobsLock) { + final ArrayMap<String, WeakReference<Resources>> resources = new ArrayMap<>(); + final Function<String, Resources> resourcesGetter = (packageName) -> { + final WeakReference<Resources> resourcesRef = resources.get(packageName); + Resources packageResources = resourcesRef == null ? null : resourcesRef.get(); + if (packageResources == null) { + packageResources = getPackageResources(mContext, packageName, userId); + resources.put(packageName, new WeakReference<>(packageResources)); + } + return packageResources; + }; + getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> { + final ArrayList<AccessorInfo> accessorInfos = new ArrayList<>(); + blobMetadata.forEachLeasee(leasee -> { + final int descriptionResId = leasee.descriptionResEntryName == null + ? Resources.ID_NULL + : getDescriptionResourceId(resourcesGetter.apply(leasee.packageName), + leasee.descriptionResEntryName, leasee.packageName); + accessorInfos.add(new AccessorInfo(leasee.packageName, leasee.expiryTimeMillis, + descriptionResId, leasee.description)); + }); + blobInfos.add(new BlobInfo(blobMetadata.getBlobId(), + blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(), accessorInfos)); + }); + } + return blobInfos; + } + + private void deleteBlobInternal(long blobId, int callingUid) { + synchronized (mBlobsLock) { + final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked( + UserHandle.getUserId(callingUid)); + userBlobs.entrySet().removeIf(entry -> { + final BlobMetadata blobMetadata = entry.getValue(); + return blobMetadata.getBlobId() == blobId; + }); + writeBlobsInfoAsync(); + } + } + private void verifyCallingPackage(int callingUid, String callingPackage) { if (mPackageManagerInternal.getPackageUid( callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) { @@ -1167,7 +1215,7 @@ public class BlobStoreManagerService extends SystemService { } @Override - public void deleteSession(@IntRange(from = 1) long sessionId, + public void abandonSession(@IntRange(from = 1) long sessionId, @NonNull String packageName) { Preconditions.checkArgumentPositive(sessionId, "sessionId must be positive: " + sessionId); @@ -1176,7 +1224,7 @@ public class BlobStoreManagerService extends SystemService { final int callingUid = Binder.getCallingUid(); verifyCallingPackage(callingUid, packageName); - deleteSessionInternal(sessionId, callingUid, packageName); + abandonSessionInternal(sessionId, callingUid, packageName); } @Override @@ -1250,6 +1298,28 @@ public class BlobStoreManagerService extends SystemService { } @Override + @NonNull + public List<BlobInfo> queryBlobsForUser(@UserIdInt int userId) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only system uid is allowed to call " + + "queryBlobsForUser()"); + } + + return queryBlobsForUserInternal(userId); + } + + @Override + public void deleteBlob(long blobId) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + throw new SecurityException("Only system uid is allowed to call " + + "deleteBlob()"); + } + + deleteBlobInternal(blobId, callingUid); + } + + @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { // TODO: add proto-based version of this. diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java new file mode 100644 index 000000000000..6af540acd6a4 --- /dev/null +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.blob; + +import static com.android.server.blob.BlobStoreConfig.TAG; + +import android.annotation.IdRes; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.util.Slog; + +class BlobStoreUtils { + private static final String DESC_RES_TYPE_STRING = "string"; + + @Nullable + static Resources getPackageResources(@NonNull Context context, + @NonNull String packageName, int userId) { + try { + return context.getPackageManager() + .getResourcesForApplicationAsUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + Slog.d(TAG, "Unknown package in user " + userId + ": " + + packageName, e); + return null; + } + } + + @IdRes + static int getDescriptionResourceId(@NonNull Resources resources, + @NonNull String resourceEntryName, @NonNull String packageName) { + return resources.getIdentifier(resourceEntryName, DESC_RES_TYPE_STRING, packageName); + } +} diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java index 0d163cf55e4e..aedba290db1f 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java @@ -40,7 +40,7 @@ public interface RuntimePermissionsPersistence { * @return the runtime permissions read */ @Nullable - RuntimePermissionsState readAsUser(@NonNull UserHandle user); + RuntimePermissionsState readForUser(@NonNull UserHandle user); /** * Write the runtime permissions to persistence. @@ -50,7 +50,8 @@ public interface RuntimePermissionsPersistence { * @param runtimePermissions the runtime permissions to write * @param user the user to write for */ - void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user); + void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, + @NonNull UserHandle user); /** * Delete the runtime permissions from persistence. @@ -59,7 +60,7 @@ public interface RuntimePermissionsPersistence { * * @param user the user to delete for */ - void deleteAsUser(@NonNull UserHandle user); + void deleteForUser(@NonNull UserHandle user); /** * Create a new instance of {@link RuntimePermissionsPersistence} implementation. diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java index 30a8b458cce5..e43f59a3377a 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java @@ -18,7 +18,7 @@ package com.android.permission.persistence; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ApexContext; +import android.content.ApexEnvironment; import android.content.pm.PackageManager; import android.os.UserHandle; import android.util.ArrayMap; @@ -67,7 +67,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers @Nullable @Override - public RuntimePermissionsState readAsUser(@NonNull UserHandle user) { + public RuntimePermissionsState readForUser(@NonNull UserHandle user) { File file = getFile(user); try (FileInputStream inputStream = new AtomicFile(file).openRead()) { XmlPullParser parser = Xml.newPullParser(); @@ -172,7 +172,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers } @Override - public void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions, + public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user) { File file = getFile(user); AtomicFile atomicFile = new AtomicFile(file); @@ -252,14 +252,14 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers } @Override - public void deleteAsUser(@NonNull UserHandle user) { + public void deleteForUser(@NonNull UserHandle user) { getFile(user).delete(); } @NonNull private static File getFile(@NonNull UserHandle user) { - ApexContext apexContext = ApexContext.getApexContext(APEX_MODULE_NAME); - File dataDirectory = apexContext.getDeviceProtectedDataDirForUser(user); + ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME); + File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user); return new File(dataDirectory, RUNTIME_PERMISSIONS_FILE_NAME); } } diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java index cd2750a0bee5..c6bfc6d32989 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java @@ -23,6 +23,7 @@ import android.annotation.SystemApi.Client; import java.util.List; import java.util.Map; +import java.util.Objects; /** * State of all runtime permissions. @@ -61,6 +62,14 @@ public final class RuntimePermissionsState { @NonNull private final Map<String, List<PermissionState>> mSharedUserPermissions; + /** + * Create a new instance of this class. + * + * @param version the version of the runtime permissions + * @param fingerprint the fingerprint of the runtime permissions + * @param packagePermissions the runtime permissions by packages + * @param sharedUserPermissions the runtime permissions by shared users + */ public RuntimePermissionsState(int version, @Nullable String fingerprint, @NonNull Map<String, List<PermissionState>> packagePermissions, @NonNull Map<String, List<PermissionState>> sharedUserPermissions) { @@ -70,32 +79,72 @@ public final class RuntimePermissionsState { mSharedUserPermissions = sharedUserPermissions; } + /** + * Get the version of the runtime permissions. + * + * @return the version of the runtime permissions + */ public int getVersion() { return mVersion; } + /** + * Get the fingerprint of the runtime permissions. + * + * @return the fingerprint of the runtime permissions + */ @Nullable public String getFingerprint() { return mFingerprint; } + /** + * Get the runtime permissions by packages. + * + * @return the runtime permissions by packages + */ @NonNull public Map<String, List<PermissionState>> getPackagePermissions() { return mPackagePermissions; } + /** + * Get the runtime permissions by shared users. + * + * @return the runtime permissions by shared users + */ @NonNull public Map<String, List<PermissionState>> getSharedUserPermissions() { return mSharedUserPermissions; } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + RuntimePermissionsState that = (RuntimePermissionsState) object; + return mVersion == that.mVersion + && Objects.equals(mFingerprint, that.mFingerprint) + && Objects.equals(mPackagePermissions, that.mPackagePermissions) + && Objects.equals(mSharedUserPermissions, that.mSharedUserPermissions); + } + + @Override + public int hashCode() { + return Objects.hash(mVersion, mFingerprint, mPackagePermissions, mSharedUserPermissions); + } + /** * State of a single permission. */ - public static class PermissionState { + public static final class PermissionState { /** - * Name of the permission. + * The name of the permission. */ @NonNull private final String mName; @@ -106,27 +155,68 @@ public final class RuntimePermissionsState { private final boolean mGranted; /** - * Flags of the permission. + * The flags of the permission. */ private final int mFlags; + /** + * Create a new instance of this class. + * + * @param name the name of the permission + * @param granted whether the permission is granted + * @param flags the flags of the permission + */ public PermissionState(@NonNull String name, boolean granted, int flags) { mName = name; mGranted = granted; mFlags = flags; } + /** + * Get the name of the permission. + * + * @return the name of the permission + */ @NonNull public String getName() { return mName; } + /** + * Get whether the permission is granted. + * + * @return whether the permission is granted + */ public boolean isGranted() { return mGranted; } + /** + * Get the flags of the permission. + * + * @return the flags of the permission + */ public int getFlags() { return mFlags; } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + PermissionState that = (PermissionState) object; + return mGranted == that.mGranted + && mFlags == that.mFlags + && Objects.equals(mName, that.mName); + } + + @Override + public int hashCode() { + return Objects.hash(mName, mGranted, mFlags); + } } } diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java index 64d6545c87cf..2e5a28aa1d6a 100644 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java +++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java @@ -40,7 +40,7 @@ public interface RolesPersistence { * @return the roles read */ @Nullable - RolesState readAsUser(@NonNull UserHandle user); + RolesState readForUser(@NonNull UserHandle user); /** * Write the roles to persistence. @@ -50,7 +50,7 @@ public interface RolesPersistence { * @param roles the roles to write * @param user the user to write for */ - void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user); + void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user); /** * Delete the roles from persistence. @@ -59,7 +59,7 @@ public interface RolesPersistence { * * @param user the user to delete for */ - void deleteAsUser(@NonNull UserHandle user); + void deleteForUser(@NonNull UserHandle user); /** * Create a new instance of {@link RolesPersistence} implementation. diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java index 3031c8213982..f66257f13ef6 100644 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java +++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java @@ -18,7 +18,7 @@ package com.android.role.persistence; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ApexContext; +import android.content.ApexEnvironment; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; @@ -65,7 +65,7 @@ public class RolesPersistenceImpl implements RolesPersistence { @Nullable @Override - public RolesState readAsUser(@NonNull UserHandle user) { + public RolesState readForUser(@NonNull UserHandle user) { File file = getFile(user); try (FileInputStream inputStream = new AtomicFile(file).openRead()) { XmlPullParser parser = Xml.newPullParser(); @@ -146,7 +146,7 @@ public class RolesPersistenceImpl implements RolesPersistence { } @Override - public void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user) { + public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) { File file = getFile(user); AtomicFile atomicFile = new AtomicFile(file); FileOutputStream outputStream = null; @@ -205,14 +205,14 @@ public class RolesPersistenceImpl implements RolesPersistence { } @Override - public void deleteAsUser(@NonNull UserHandle user) { + public void deleteForUser(@NonNull UserHandle user) { getFile(user).delete(); } @NonNull private static File getFile(@NonNull UserHandle user) { - ApexContext apexContext = ApexContext.getApexContext(APEX_MODULE_NAME); - File dataDirectory = apexContext.getDeviceProtectedDataDirForUser(user); + ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME); + File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user); return new File(dataDirectory, ROLES_FILE_NAME); } } diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java index 7da9d11f172f..f61efa0e840d 100644 --- a/apex/permission/service/java/com/android/role/persistence/RolesState.java +++ b/apex/permission/service/java/com/android/role/persistence/RolesState.java @@ -22,6 +22,7 @@ import android.annotation.SystemApi; import android.annotation.SystemApi.Client; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -50,6 +51,13 @@ public final class RolesState { @NonNull private final Map<String, Set<String>> mRoles; + /** + * Create a new instance of this class. + * + * @param version the version of the roles + * @param packagesHash the hash of all packages in the system + * @param roles the roles + */ public RolesState(int version, @Nullable String packagesHash, @NonNull Map<String, Set<String>> roles) { mVersion = version; @@ -57,17 +65,51 @@ public final class RolesState { mRoles = roles; } + /** + * Get the version of the roles. + * + * @return the version of the roles + */ public int getVersion() { return mVersion; } + /** + * Get the hash of all packages in the system. + * + * @return the hash of all packages in the system + */ @Nullable public String getPackagesHash() { return mPackagesHash; } + /** + * Get the roles. + * + * @return the roles + */ @NonNull public Map<String, Set<String>> getRoles() { return mRoles; } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + RolesState that = (RolesState) object; + return mVersion == that.mVersion + && Objects.equals(mPackagesHash, that.mPackagesHash) + && Objects.equals(mRoles, that.mRoles); + } + + @Override + public int hashCode() { + return Objects.hash(mVersion, mPackagesHash, mRoles); + } } diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index dc61f2aedf99..c84627de3e36 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -54,6 +54,7 @@ import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -102,9 +103,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private final OnAlarmListener mAnomalyAlarmListener = new AnomalyAlarmListener(); private final OnAlarmListener mPullingAlarmListener = new PullingAlarmListener(); private final OnAlarmListener mPeriodicAlarmListener = new PeriodicAlarmListener(); - private final BroadcastReceiver mAppUpdateReceiver; - private final BroadcastReceiver mUserUpdateReceiver; - private final ShutdownEventReceiver mShutdownEventReceiver; private StatsManagerService mStatsManagerService; @@ -118,27 +116,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { super(); mContext = context; mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - mAppUpdateReceiver = new AppUpdateReceiver(); - mUserUpdateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - synchronized (sStatsdLock) { - if (sStatsd == null) { - Log.w(TAG, "Could not access statsd for UserUpdateReceiver"); - return; - } - try { - // Pull the latest state of UID->app name, version mapping. - // Needed since the new user basically has a version of every app. - informAllUidsLocked(context); - } catch (RemoteException e) { - Log.e(TAG, "Failed to inform statsd latest update of all apps", e); - forgetEverythingLocked(); - } - } - } - }; - mShutdownEventReceiver = new ShutdownEventReceiver(); if (DEBUG) Log.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED."); HandlerThread handlerThread = new HandlerThread(TAG); handlerThread.start(); @@ -162,9 +139,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { return ret; } - // Assumes that sStatsdLock is held. - @GuardedBy("sStatsdLock") - private void informAllUidsLocked(Context context) throws RemoteException { + /** + * Non-blocking call to retrieve a reference to statsd + * + * @return IStatsd object if statsd is ready, null otherwise. + */ + private static IStatsd getStatsdNonblocking() { + synchronized (sStatsdLock) { + return sStatsd; + } + } + + private static void informAllUidsLocked(Context context) throws RemoteException { UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); PackageManager pm = context.getPackageManager(); final List<UserHandle> users = um.getUserHandles(true); @@ -273,7 +259,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (!replacing) { // Don't bother sending an update if we're right about to get another // intent for the new version that's added. - PackageManager pm = context.getPackageManager(); String app = intent.getData().getSchemeSpecificPart(); sStatsd.informOnePackageRemoved(app, uid); } @@ -303,23 +288,43 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - public final static class AnomalyAlarmListener implements OnAlarmListener { + private static final class UserUpdateReceiver extends BroadcastReceiver { @Override - public void onAlarm() { - Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time " - + System.currentTimeMillis() + "ms."); + public void onReceive(Context context, Intent intent) { synchronized (sStatsdLock) { if (sStatsd == null) { - Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing"); + Log.w(TAG, "Could not access statsd for UserUpdateReceiver"); return; } try { - // Two-way call to statsd to retain AlarmManager wakelock - sStatsd.informAnomalyAlarmFired(); + // Pull the latest state of UID->app name, version mapping. + // Needed since the new user basically has a version of every app. + informAllUidsLocked(context); } catch (RemoteException e) { - Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e); + Log.e(TAG, "Failed to inform statsd latest update of all apps", e); } } + } + } + + public static final class AnomalyAlarmListener implements OnAlarmListener { + @Override + public void onAlarm() { + if (DEBUG) { + Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time " + + System.currentTimeMillis() + "ms."); + } + IStatsd statsd = getStatsdNonblocking(); + if (statsd == null) { + Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing"); + return; + } + try { + // Two-way call to statsd to retain AlarmManager wakelock + statsd.informAnomalyAlarmFired(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e); + } // AlarmManager releases its own wakelock here. } } @@ -330,17 +335,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (DEBUG) { Log.d(TAG, "Time to poll something."); } - synchronized (sStatsdLock) { - if (sStatsd == null) { - Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing."); - return; - } - try { - // Two-way call to statsd to retain AlarmManager wakelock - sStatsd.informPollAlarmFired(); - } catch (RemoteException e) { - Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e); - } + IStatsd statsd = getStatsdNonblocking(); + if (statsd == null) { + Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing."); + return; + } + try { + // Two-way call to statsd to retain AlarmManager wakelock + statsd.informPollAlarmFired(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e); } } } @@ -351,17 +355,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (DEBUG) { Log.d(TAG, "Time to trigger periodic alarm."); } - synchronized (sStatsdLock) { - if (sStatsd == null) { - Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing."); - return; - } - try { - // Two-way call to statsd to retain AlarmManager wakelock - sStatsd.informAlarmForSubscriberTriggeringFired(); - } catch (RemoteException e) { - Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e); - } + IStatsd statsd = getStatsdNonblocking(); + if (statsd == null) { + Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing."); + return; + } + try { + // Two-way call to statsd to retain AlarmManager wakelock + statsd.informAlarmForSubscriberTriggeringFired(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e); } // AlarmManager releases its own wakelock here. } @@ -379,17 +382,19 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { return; } - Log.i(TAG, "StatsCompanionService noticed a shutdown."); - synchronized (sStatsdLock) { - if (sStatsd == null) { - Log.w(TAG, "Could not access statsd to inform it of a shutdown event."); - return; - } - try { - sStatsd.informDeviceShutdown(); - } catch (Exception e) { - Log.w(TAG, "Failed to inform statsd of a shutdown event.", e); - } + if (DEBUG) { + Log.i(TAG, "StatsCompanionService noticed a shutdown."); + } + IStatsd statsd = getStatsdNonblocking(); + if (statsd == null) { + Log.w(TAG, "Could not access statsd to inform it of a shutdown event."); + return; + } + try { + // two way binder call + statsd.informDeviceShutdown(); + } catch (Exception e) { + Log.w(TAG, "Failed to inform statsd of a shutdown event.", e); } } } @@ -515,7 +520,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - @Override + @Override // Binder call public void triggerUidSnapshot() { StatsCompanion.enforceStatsdCallingUid(); synchronized (sStatsdLock) { @@ -525,7 +530,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } catch (RemoteException e) { Log.e(TAG, "Failed to trigger uid snapshot.", e); } finally { - restoreCallingIdentity(token); + Binder.restoreCallingIdentity(token); } } } @@ -539,15 +544,28 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { // Statsd related code /** - * Fetches the statsd IBinder service. - * Note: This should only be called from sayHiToStatsd. All other clients should use the cached - * sStatsd with a null check. + * Fetches the statsd IBinder service. This is a blocking call. + * Note: This should only be called from {@link #sayHiToStatsd()}. All other clients should use + * the cached sStatsd via {@link #getStatsdNonblocking()}. */ - private static IStatsd fetchStatsdService() { - return IStatsd.Stub.asInterface(StatsFrameworkInitializer - .getStatsServiceManager() - .getStatsdServiceRegisterer() - .get()); + private IStatsd fetchStatsdService(StatsdDeathRecipient deathRecipient) { + synchronized (sStatsdLock) { + if (sStatsd == null) { + sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer + .getStatsServiceManager() + .getStatsdServiceRegisterer() + .get()); + if (sStatsd != null) { + try { + sStatsd.asBinder().linkToDeath(deathRecipient, /* flags */ 0); + } catch (RemoteException e) { + Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed"); + statsdNotReadyLocked(); + } + } + } + return sStatsd; + } } /** @@ -567,67 +585,84 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { * statsd. */ private void sayHiToStatsd() { - synchronized (sStatsdLock) { - if (sStatsd != null) { - Log.e(TAG, "Trying to fetch statsd, but it was already fetched", - new IllegalStateException( - "sStatsd is not null when being fetched")); - return; - } - sStatsd = fetchStatsdService(); - if (sStatsd == null) { - Log.i(TAG, - "Could not yet find statsd to tell it that StatsCompanion is " - + "alive."); - return; - } - mStatsManagerService.statsdReady(sStatsd); - if (DEBUG) Log.d(TAG, "Saying hi to statsd"); + if (getStatsdNonblocking() != null) { + Log.e(TAG, "Trying to fetch statsd, but it was already fetched", + new IllegalStateException( + "sStatsd is not null when being fetched")); + return; + } + StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient(); + IStatsd statsd = fetchStatsdService(deathRecipient); + if (statsd == null) { + Log.i(TAG, + "Could not yet find statsd to tell it that StatsCompanion is " + + "alive."); + return; + } + mStatsManagerService.statsdReady(statsd); + if (DEBUG) Log.d(TAG, "Saying hi to statsd"); + try { + statsd.statsCompanionReady(); + + cancelAnomalyAlarm(); + cancelPullingAlarm(); + + BroadcastReceiver appUpdateReceiver = new AppUpdateReceiver(); + BroadcastReceiver userUpdateReceiver = new UserUpdateReceiver(); + BroadcastReceiver shutdownEventReceiver = new ShutdownEventReceiver(); + + // Setup broadcast receiver for updates. + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED); + filter.addAction(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null); + + // Setup receiver for user initialize (which happens once for a new user) + // and + // if a user is removed. + filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE); + filter.addAction(Intent.ACTION_USER_REMOVED); + mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null); + + // Setup receiver for device reboots or shutdowns. + filter = new IntentFilter(Intent.ACTION_REBOOT); + filter.addAction(Intent.ACTION_SHUTDOWN); + mContext.registerReceiverForAllUsers( + shutdownEventReceiver, filter, null, null); + + // Only add the receivers if the registration is successful. + deathRecipient.addRegisteredBroadcastReceivers( + List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver)); + + final long token = Binder.clearCallingIdentity(); try { - sStatsd.statsCompanionReady(); - // If the statsCompanionReady two-way binder call returns, link to statsd. - try { - sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0); - } catch (RemoteException e) { - Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e); - forgetEverythingLocked(); - } - // Setup broadcast receiver for updates. - IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED); - filter.addAction(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addDataScheme("package"); - mContext.registerReceiverForAllUsers(mAppUpdateReceiver, filter, null, null); - - // Setup receiver for user initialize (which happens once for a new user) - // and - // if a user is removed. - filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE); - filter.addAction(Intent.ACTION_USER_REMOVED); - mContext.registerReceiverForAllUsers(mUserUpdateReceiver, filter, null, null); - - // Setup receiver for device reboots or shutdowns. - filter = new IntentFilter(Intent.ACTION_REBOOT); - filter.addAction(Intent.ACTION_SHUTDOWN); - mContext.registerReceiverForAllUsers( - mShutdownEventReceiver, filter, null, null); - final long token = Binder.clearCallingIdentity(); - try { - // Pull the latest state of UID->app name, version mapping when - // statsd starts. - informAllUidsLocked(mContext); - } finally { - restoreCallingIdentity(token); - } - Log.i(TAG, "Told statsd that StatsCompanionService is alive."); - } catch (RemoteException e) { - Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e); - forgetEverythingLocked(); + // Pull the latest state of UID->app name, version mapping when + // statsd starts. + informAllUidsLocked(mContext); + } finally { + Binder.restoreCallingIdentity(token); } + Log.i(TAG, "Told statsd that StatsCompanionService is alive."); + } catch (RemoteException e) { + Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e); } } private class StatsdDeathRecipient implements IBinder.DeathRecipient { + + private List<BroadcastReceiver> mReceiversToUnregister; + + StatsdDeathRecipient() { + mReceiversToUnregister = new ArrayList<>(); + } + + public void addRegisteredBroadcastReceivers(List<BroadcastReceiver> receivers) { + synchronized (sStatsdLock) { + mReceiversToUnregister.addAll(receivers); + } + } + @Override public void binderDied() { Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers"); @@ -656,20 +691,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } } - forgetEverythingLocked(); + // We only unregister in binder death becaseu receivers can only be unregistered + // once, or an IllegalArgumentException is thrown. + for (BroadcastReceiver receiver: mReceiversToUnregister) { + mContext.unregisterReceiver(receiver); + } + statsdNotReadyLocked(); } } } - @GuardedBy("StatsCompanionService.sStatsdLock") - private void forgetEverythingLocked() { + private void statsdNotReadyLocked() { sStatsd = null; - mContext.unregisterReceiver(mAppUpdateReceiver); - mContext.unregisterReceiver(mUserUpdateReceiver); - mContext.unregisterReceiver(mShutdownEventReceiver); - cancelAnomalyAlarm(); - cancelPullingAlarm(); - mStatsManagerService.statsdNotReady(); } diff --git a/api/current.txt b/api/current.txt index 59afab200fff..06dd88b033a1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4368,7 +4368,7 @@ package android.app { method @Deprecated public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int); method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String); method @Nullable public static String permissionToOp(@NonNull String); - method public void setNotedAppOpsCollector(@Nullable android.app.AppOpsManager.AppOpsCollector); + method public void setOnOpNotedCallback(@Nullable java.util.concurrent.Executor, @Nullable android.app.AppOpsManager.OnOpNotedCallback); method @Deprecated public int startOp(@NonNull String, int, @NonNull String); method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String); method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String); @@ -4424,14 +4424,6 @@ package android.app { field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1 } - public abstract static class AppOpsManager.AppOpsCollector { - ctor public AppOpsManager.AppOpsCollector(); - method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor(); - method public abstract void onAsyncNoted(@NonNull android.app.AsyncNotedAppOp); - method public abstract void onNoted(@NonNull android.app.SyncNotedAppOp); - method public abstract void onSelfNoted(@NonNull android.app.SyncNotedAppOp); - } - public static interface AppOpsManager.OnOpActiveChangedListener { method public void onOpActiveChanged(@NonNull String, int, @NonNull String, boolean); } @@ -4440,6 +4432,13 @@ package android.app { method public void onOpChanged(String, String); } + public abstract static class AppOpsManager.OnOpNotedCallback { + ctor public AppOpsManager.OnOpNotedCallback(); + method public abstract void onAsyncNoted(@NonNull android.app.AsyncNotedAppOp); + method public abstract void onNoted(@NonNull android.app.SyncNotedAppOp); + method public abstract void onSelfNoted(@NonNull android.app.SyncNotedAppOp); + } + public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 { ctor public Application(); method public static String getProcessName(); @@ -4591,7 +4590,7 @@ package android.app { method @NonNull public String getMessage(); method @IntRange(from=0) public int getNotingUid(); method @NonNull public String getOp(); - method @IntRange(from=0) public long getTime(); + method public long getTime(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AsyncNotedAppOp> CREATOR; } @@ -7583,12 +7582,12 @@ package android.app.blob { } public class BlobStoreManager { + method public void abandonSession(@IntRange(from=1) long) throws java.io.IOException; method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int, long) throws java.io.IOException; method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence, long) throws java.io.IOException; method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int) throws java.io.IOException; method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence) throws java.io.IOException; method @IntRange(from=1) public long createSession(@NonNull android.app.blob.BlobHandle) throws java.io.IOException; - method public void deleteSession(@IntRange(from=1) long) throws java.io.IOException; method @NonNull public android.os.ParcelFileDescriptor openBlob(@NonNull android.app.blob.BlobHandle) throws java.io.IOException; method @NonNull public android.app.blob.BlobStoreManager.Session openSession(@IntRange(from=1) long) throws java.io.IOException; method public void releaseLease(@NonNull android.app.blob.BlobHandle) throws java.io.IOException; @@ -9467,7 +9466,7 @@ package android.companion { public static final class WifiDeviceFilter.Builder { ctor public WifiDeviceFilter.Builder(); method @NonNull public android.companion.WifiDeviceFilter build(); - method @NonNull public android.companion.WifiDeviceFilter.Builder setBssid(@Nullable android.net.MacAddress); + method @NonNull public android.companion.WifiDeviceFilter.Builder setBssid(@NonNull android.net.MacAddress); method @NonNull public android.companion.WifiDeviceFilter.Builder setBssidMask(@NonNull android.net.MacAddress); method @NonNull public android.companion.WifiDeviceFilter.Builder setNamePattern(@Nullable java.util.regex.Pattern); } @@ -9882,7 +9881,7 @@ package android.content { method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver); method @Deprecated public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, boolean); method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, int); - method public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int); + method public void notifyChange(@NonNull java.util.Collection<android.net.Uri>, @Nullable android.database.ContentObserver, int); method @Nullable public final android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException; method @Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException; method @Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException; @@ -10566,6 +10565,7 @@ package android.content { field public static final String ACTION_APP_ERROR = "android.intent.action.APP_ERROR"; field public static final String ACTION_ASSIST = "android.intent.action.ASSIST"; field public static final String ACTION_ATTACH_DATA = "android.intent.action.ATTACH_DATA"; + field public static final String ACTION_AUTO_REVOKE_PERMISSIONS = "android.intent.action.AUTO_REVOKE_PERMISSIONS"; field public static final String ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED"; field public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW"; field public static final String ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY"; @@ -12972,11 +12972,11 @@ package android.database { method @Deprecated public final void dispatchChange(boolean); method public final void dispatchChange(boolean, @Nullable android.net.Uri); method public final void dispatchChange(boolean, @Nullable android.net.Uri, int); - method public final void dispatchChange(boolean, @NonNull Iterable<android.net.Uri>, int); + method public final void dispatchChange(boolean, @NonNull java.util.Collection<android.net.Uri>, int); method public void onChange(boolean); method public void onChange(boolean, @Nullable android.net.Uri); method public void onChange(boolean, @Nullable android.net.Uri, int); - method public void onChange(boolean, @NonNull Iterable<android.net.Uri>, int); + method public void onChange(boolean, @NonNull java.util.Collection<android.net.Uri>, int); } public interface CrossProcessCursor extends android.database.Cursor { @@ -18118,9 +18118,7 @@ package android.hardware.fingerprint { ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull java.security.Signature); ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Cipher); ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Mac); - ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull android.security.identity.IdentityCredential); method @Deprecated public javax.crypto.Cipher getCipher(); - method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential(); method @Deprecated public javax.crypto.Mac getMac(); method @Deprecated public java.security.Signature getSignature(); } @@ -27055,7 +27053,8 @@ package android.media { method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback, @NonNull android.media.RouteDiscoveryPreference); method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback); method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener); - method public void transferTo(@Nullable android.media.MediaRoute2Info); + method public void stop(); + method public void transferTo(@NonNull android.media.MediaRoute2Info); method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback); method public void unregisterRouteCallback(@NonNull android.media.MediaRouter2.RouteCallback); method public void unregisterTransferCallback(@NonNull android.media.MediaRouter2.TransferCallback); @@ -27095,8 +27094,9 @@ package android.media { public abstract static class MediaRouter2.TransferCallback { ctor public MediaRouter2.TransferCallback(); + method public void onStopped(@NonNull android.media.MediaRouter2.RoutingController); method public void onTransferFailed(@NonNull android.media.MediaRoute2Info); - method public void onTransferred(@NonNull android.media.MediaRouter2.RoutingController, @Nullable android.media.MediaRouter2.RoutingController); + method public void onTransferred(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRouter2.RoutingController); } public class MediaScannerConnection implements android.content.ServiceConnection { @@ -36154,6 +36154,8 @@ package android.os { method public static boolean isExternalStorageEmulated(@NonNull java.io.File); method public static boolean isExternalStorageLegacy(); method public static boolean isExternalStorageLegacy(@NonNull java.io.File); + method public static boolean isExternalStorageManager(); + method public static boolean isExternalStorageManager(@NonNull java.io.File); method public static boolean isExternalStorageRemovable(); method public static boolean isExternalStorageRemovable(@NonNull java.io.File); field public static String DIRECTORY_ALARMS; @@ -42690,7 +42692,7 @@ package android.security.identity { ctor public PersonalizationData.Builder(); method @NonNull public android.security.identity.PersonalizationData.Builder addAccessControlProfile(@NonNull android.security.identity.AccessControlProfile); method @NonNull public android.security.identity.PersonalizationData build(); - method @NonNull public android.security.identity.PersonalizationData.Builder setEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]); + method @NonNull public android.security.identity.PersonalizationData.Builder putEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]); } public abstract class ResultData { @@ -42698,7 +42700,7 @@ package android.security.identity { method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String); method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String); method @Nullable public abstract byte[] getMessageAuthenticationCode(); - method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaceNames(); + method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces(); method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String); method @NonNull public abstract byte[] getStaticAuthenticationData(); method public abstract int getStatus(@NonNull String, @NonNull String); @@ -43110,7 +43112,7 @@ package android.service.autofill { public static final class FillResponse.Builder { ctor public FillResponse.Builder(); method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset); - method @NonNull public android.service.autofill.FillResponse.Builder addInlineAction(@NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.FillResponse.Builder addInlineAction(@NonNull android.service.autofill.InlineAction); method @NonNull public android.service.autofill.FillResponse build(); method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long); method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews); @@ -43140,6 +43142,15 @@ package android.service.autofill { method public android.service.autofill.ImageTransformation build(); } + public final class InlineAction implements android.os.Parcelable { + ctor public InlineAction(@NonNull android.service.autofill.InlinePresentation, @NonNull android.content.IntentSender); + method public int describeContents(); + method @NonNull public android.content.IntentSender getAction(); + method @NonNull public android.service.autofill.InlinePresentation getInlinePresentation(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.InlineAction> CREATOR; + } + public final class InlinePresentation implements android.os.Parcelable { ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec, boolean); method public int describeContents(); @@ -46585,12 +46596,9 @@ package android.telephony { field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX"; field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX"; field public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int"; - field public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string"; - field public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT = "5g_icon_display_grace_period_sec_int"; field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array"; field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array"; field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array"; - field public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long"; field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool"; field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call"; @@ -46783,7 +46791,6 @@ package android.telephony { field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool"; field public static final String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL = "show_blocking_pay_phone_option_bool"; field public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool"; - field public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = "show_carrier_data_icon_pattern_string"; field public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool"; field public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool"; field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool"; @@ -47563,7 +47570,6 @@ package android.telephony { method @NonNull public java.util.List<java.lang.Integer> getAvailableServices(); method @Nullable public android.telephony.CellIdentity getCellIdentity(); method public int getDomain(); - method public int getNrState(); method @Nullable public String getRegisteredPlmn(); method public int getTransportType(); method public boolean isRegistered(); @@ -47766,9 +47772,7 @@ package android.telephony { method public boolean getIsManualSelection(); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList(); method public String getOperatorAlphaLong(); - method @Nullable public String getOperatorAlphaLongRaw(); method public String getOperatorAlphaShort(); - method @Nullable public String getOperatorAlphaShortRaw(); method public String getOperatorNumeric(); method public boolean getRoaming(); method public int getState(); @@ -48040,13 +48044,13 @@ package android.telephony { method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>); method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context); method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList(); - method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList(); method public static int getActiveDataSubscriptionId(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount(); method public int getActiveSubscriptionInfoCountMax(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList(); + method @NonNull public java.util.List<android.telephony.SubscriptionInfo> getCompleteActiveSubscriptionInfoList(); method public static int getDefaultDataSubscriptionId(); method public static int getDefaultSmsSubscriptionId(); method public static int getDefaultSubscriptionId(); @@ -48209,6 +48213,7 @@ package android.telephony { method public boolean isEmergencyNumber(@NonNull String); method public boolean isHearingAidCompatibilitySupported(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed(); + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported(); method public boolean isNetworkRoaming(); method public boolean isRttSupported(); @@ -55578,10 +55583,12 @@ package android.view { } public interface WindowInsetsController { + method public void addOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener); method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener); method public int getSystemBarsAppearance(); method public int getSystemBarsBehavior(); method public void hide(int); + method public void removeOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener); method public void setSystemBarsAppearance(int, int); method public void setSystemBarsBehavior(int); method public void show(int); @@ -55592,6 +55599,10 @@ package android.view { field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2 } + public static interface WindowInsetsController.OnControllableInsetsChangedListener { + method public void onControllableInsetsChanged(@NonNull android.view.WindowInsetsController, int); + } + public interface WindowManager extends android.view.ViewManager { method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics(); method @Deprecated public android.view.Display getDefaultDisplay(); @@ -56157,7 +56168,7 @@ package android.view.accessibility { } public static final class AccessibilityNodeInfo.ExtraRenderingInfo { - method @Nullable public android.util.Size getLayoutParams(); + method @Nullable public android.util.Size getLayoutSize(); method public float getTextSizeInPx(); method public int getTextSizeUnit(); } @@ -57991,7 +58002,7 @@ package android.webkit { method @Deprecated public abstract void removeSessionCookie(); method public abstract void removeSessionCookies(@Nullable android.webkit.ValueCallback<java.lang.Boolean>); method public abstract void setAcceptCookie(boolean); - method public static void setAcceptFileSchemeCookies(boolean); + method @Deprecated public static void setAcceptFileSchemeCookies(boolean); method public abstract void setAcceptThirdPartyCookies(android.webkit.WebView, boolean); method public abstract void setCookie(String, String); method public abstract void setCookie(String, String, @Nullable android.webkit.ValueCallback<java.lang.Boolean>); @@ -58356,8 +58367,8 @@ package android.webkit { method public abstract String getUserAgentString(); method public abstract void setAllowContentAccess(boolean); method public abstract void setAllowFileAccess(boolean); - method public abstract void setAllowFileAccessFromFileURLs(boolean); - method public abstract void setAllowUniversalAccessFromFileURLs(boolean); + method @Deprecated public abstract void setAllowFileAccessFromFileURLs(boolean); + method @Deprecated public abstract void setAllowUniversalAccessFromFileURLs(boolean); method public abstract void setAppCacheEnabled(boolean); method @Deprecated public abstract void setAppCacheMaxSize(long); method public abstract void setAppCachePath(String); @@ -82223,4 +82234,3 @@ package org.xmlpull.v1.sax2 { } } - diff --git a/api/removed.txt b/api/removed.txt index fb6d57694c78..8537b21eb438 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -69,6 +69,10 @@ package android.app.usage { package android.content { + public abstract class ContentResolver { + method @Deprecated public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int); + } + public abstract class Context { method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); method public abstract java.io.File getSharedPreferencesPath(String); diff --git a/api/system-current.txt b/api/system-current.txt index 9448fb136b8c..9b06bf14d8fb 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -79,6 +79,7 @@ package android { field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER"; field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE"; field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED"; + field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS"; field public static final String FORCE_BACK = "android.permission.FORCE_BACK"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS"; @@ -231,6 +232,7 @@ package android { field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS"; field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK"; field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES"; + field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS"; field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY"; field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS"; @@ -388,6 +390,7 @@ package android.app { field public static final String OPSTR_AUDIO_NOTIFICATION_VOLUME = "android:audio_notification_volume"; field public static final String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume"; field public static final String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume"; + field public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = "android:auto_revoke_permissions_if_unused"; field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service"; field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state"; field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts"; @@ -1758,8 +1761,8 @@ package android.companion { package android.content { - public class ApexContext { - method @NonNull public static android.content.ApexContext getApexContext(@NonNull String); + public class ApexEnvironment { + method @NonNull public static android.content.ApexEnvironment getApexEnvironment(@NonNull String); method @NonNull public java.io.File getCredentialProtectedDataDirForUser(@NonNull android.os.UserHandle); method @NonNull public java.io.File getDeviceProtectedDataDir(); method @NonNull public java.io.File getDeviceProtectedDataDirForUser(@NonNull android.os.UserHandle); @@ -2215,9 +2218,7 @@ package android.content.pm { field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000 field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000 - field public static final int FLAG_PERMISSION_AUTO_REVOKED = 1048576; // 0x100000 - field public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 131072; // 0x20000 - field public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 262144; // 0x40000 + field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000 field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20 field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000 field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000 @@ -2301,7 +2302,7 @@ package android.content.pm { method public void onPermissionsChanged(int); } - @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { + @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { } public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { @@ -2939,7 +2940,7 @@ package android.hardware.lights { public final class LightsManager.LightsSession implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest); } public final class LightsRequest { @@ -4986,7 +4987,7 @@ package android.media.tv.tuner.dvr { package android.media.tv.tuner.filter { - public class AlpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { + public final class AlpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.AlpFilterConfiguration.Builder builder(@NonNull android.content.Context); method public int getLengthType(); method public int getPacketType(); @@ -5001,10 +5002,11 @@ package android.media.tv.tuner.filter { field public static final int PACKET_TYPE_SIGNALING = 4; // 0x4 } - public static class AlpFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.AlpFilterConfiguration.Builder> { + public static final class AlpFilterConfiguration.Builder { method @NonNull public android.media.tv.tuner.filter.AlpFilterConfiguration build(); method @NonNull public android.media.tv.tuner.filter.AlpFilterConfiguration.Builder setLengthType(int); method @NonNull public android.media.tv.tuner.filter.AlpFilterConfiguration.Builder setPacketType(int); + method @NonNull public android.media.tv.tuner.filter.AlpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings); } public class AudioDescriptor { @@ -5092,15 +5094,11 @@ package android.media.tv.tuner.filter { method public abstract int getType(); } - public abstract static class FilterConfiguration.Builder<T extends android.media.tv.tuner.filter.FilterConfiguration.Builder<T>> { - method @NonNull public T setSettings(@Nullable android.media.tv.tuner.filter.Settings); - } - public abstract class FilterEvent { ctor public FilterEvent(); } - public class IpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { + public final class IpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder(@NonNull android.content.Context); method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress(); method public int getDstPort(); @@ -5110,11 +5108,12 @@ package android.media.tv.tuner.filter { method public boolean isPassthrough(); } - public static class IpFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.IpFilterConfiguration.Builder> { + public static final class IpFilterConfiguration.Builder { method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration build(); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstIpAddress(@NonNull byte[]); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstPort(int); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setPassthrough(boolean); + method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcIpAddress(@NonNull byte[]); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcPort(int); } @@ -5138,15 +5137,16 @@ package android.media.tv.tuner.filter { method public boolean isSecureMemory(); } - public class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { + public final class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.MmtpFilterConfiguration.Builder builder(@NonNull android.content.Context); method public int getMmtpPacketId(); method public int getType(); } - public static class MmtpFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.MmtpFilterConfiguration.Builder> { + public static final class MmtpFilterConfiguration.Builder { method @NonNull public android.media.tv.tuner.filter.MmtpFilterConfiguration build(); method @NonNull public android.media.tv.tuner.filter.MmtpFilterConfiguration.Builder setMmtpPacketId(int); + method @NonNull public android.media.tv.tuner.filter.MmtpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings); } public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent { @@ -5281,7 +5281,7 @@ package android.media.tv.tuner.filter { field public static final long TIMESTAMP_UNAVAILABLE = -1L; // 0xffffffffffffffffL } - public class TlvFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { + public final class TlvFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.TlvFilterConfiguration.Builder builder(@NonNull android.content.Context); method public int getPacketType(); method public int getType(); @@ -5294,21 +5294,23 @@ package android.media.tv.tuner.filter { field public static final int PACKET_TYPE_SIGNALING = 254; // 0xfe } - public static class TlvFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TlvFilterConfiguration.Builder> { + public static final class TlvFilterConfiguration.Builder { method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration build(); method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setCompressedIpPacket(boolean); method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPacketType(int); method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPassthrough(boolean); + method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings); } - public class TsFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { + public final class TsFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.TsFilterConfiguration.Builder builder(@NonNull android.content.Context); method public int getTpid(); method public int getType(); } - public static class TsFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TsFilterConfiguration.Builder> { + public static final class TsFilterConfiguration.Builder { method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration build(); + method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings); method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setTpid(int); } @@ -6127,7 +6129,7 @@ package android.net { } public class EthernetManager { - method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback); } public static interface EthernetManager.TetheredInterfaceCallback { @@ -7240,7 +7242,6 @@ package android.net.wifi { field @Deprecated public int numScorerOverride; field @Deprecated public int numScorerOverrideAndSwitchedNetwork; field @Deprecated public boolean requirePmf; - field @Deprecated @Nullable public String saePasswordId; field @Deprecated public boolean shared; field @Deprecated public boolean useExternalScores; } @@ -7328,17 +7329,17 @@ package android.net.wifi { method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK}) public void disableEphemeralNetwork(@NonNull String); - method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void factoryReset(); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>); - method @Nullable @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public String getCountryCode(); + method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork(); method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>); method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); - method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration(); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration getSoftApConfiguration(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener); method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState(); @@ -7459,9 +7460,9 @@ package android.net.wifi { method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry); } - public static interface WifiManager.ScoreChangeCallback { - method public void onScoreChange(int, int); - method public void onTriggerUpdateOfWifiUsabilityStats(int); + public static interface WifiManager.ScoreUpdateObserver { + method public void notifyScoreUpdate(int, int); + method public void triggerUpdateOfWifiUsabilityStats(int); } public static interface WifiManager.SoftApCallback { @@ -7481,9 +7482,9 @@ package android.net.wifi { } public static interface WifiManager.WifiConnectedNetworkScorer { - method public void setScoreChangeCallback(@NonNull android.net.wifi.WifiManager.ScoreChangeCallback); - method public void start(int); - method public void stop(int); + method public void onSetScoreUpdateObserver(@NonNull android.net.wifi.WifiManager.ScoreUpdateObserver); + method public void onStart(int); + method public void onStop(int); } public final class WifiMigration { @@ -7815,9 +7816,6 @@ package android.net.wifi.nl80211 { method public int getMaxNumberTxSpatialStreams(); method public boolean isChannelWidthSupported(int); method public boolean isWifiStandardSupported(int); - method public void setChannelWidthSupported(int, boolean); - method public void setMaxNumberRxSpatialStreams(int); - method public void setMaxNumberTxSpatialStreams(int); method public void setWifiStandardSupport(int, boolean); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.nl80211.DeviceWiphyCapabilities> CREATOR; @@ -7988,7 +7986,7 @@ package android.net.wifi.p2p { method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public void requestPersistentGroupInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setDeviceName(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull String, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); - method @RequiresPermission(allOf={android.Manifest.permission.CONNECTIVITY_INTERNAL, android.Manifest.permission.CONFIGURE_WIFI_DISPLAY}) public void setMiracastMode(int); + method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setMiracastMode(int); method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pWfdInfo, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setWifiP2pChannels(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void startListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); @@ -8947,7 +8945,7 @@ package android.permission { } public final class PermissionManager { - method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public int getRuntimePermissionsVersion(); + method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion(); method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledCarrierApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledImsServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); @@ -8955,7 +8953,7 @@ package android.permission { method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); + method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int); method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String); } @@ -9438,7 +9436,6 @@ package android.provider { public static final class Telephony.Carriers implements android.provider.BaseColumns { field public static final String APN_SET_ID = "apn_set_id"; - field @Deprecated public static final String BEARER_BITMASK = "bearer_bitmask"; field public static final int CARRIER_EDITED = 4; // 0x4 field @NonNull public static final android.net.Uri DPC_URI; field public static final String EDITED_STATUS = "edited"; @@ -9447,11 +9444,6 @@ package android.provider { field public static final String MODEM_PERSIST = "modem_cognitive"; field public static final String MTU = "mtu"; field public static final int NO_APN_SET_ID = 0; // 0x0 - field public static final String PROFILE_ID = "profile_id"; - field public static final String SKIP_464XLAT = "skip_464xlat"; - field public static final int SKIP_464XLAT_DEFAULT = -1; // 0xffffffff - field public static final int SKIP_464XLAT_DISABLE = 0; // 0x0 - field public static final int SKIP_464XLAT_ENABLE = 1; // 0x1 field public static final String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time"; field public static final int UNEDITED = 0; // 0x0 field public static final int USER_DELETED = 2; // 0x2 @@ -9866,10 +9858,10 @@ package android.service.autofill.augmented { public static final class FillResponse.Builder { ctor public FillResponse.Builder(); method @NonNull public android.service.autofill.augmented.FillResponse build(); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@Nullable java.util.List<android.service.autofill.InlinePresentation>); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>); + method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@NonNull android.os.Bundle); + method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@NonNull android.service.autofill.augmented.FillWindow); + method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@NonNull java.util.List<android.service.autofill.InlineAction>); + method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@NonNull java.util.List<android.service.autofill.Dataset>); } public final class FillWindow implements java.lang.AutoCloseable { @@ -10915,19 +10907,6 @@ package android.telephony { method @NonNull public java.util.List<android.telephony.CbGeoUtils.LatLng> getVertices(); } - public final class CdmaEriInformation implements android.os.Parcelable { - method public int describeContents(); - method public int getEriIconIndex(); - method public int getEriIconMode(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CdmaEriInformation> CREATOR; - field public static final int ERI_FLASH = 2; // 0x2 - field public static final int ERI_ICON_MODE_FLASH = 1; // 0x1 - field public static final int ERI_ICON_MODE_NORMAL = 0; // 0x0 - field public static final int ERI_OFF = 1; // 0x1 - field public static final int ERI_ON = 0; // 0x0 - } - public class CellBroadcastIntents { method public static void sendSmsCbReceivedBroadcast(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.telephony.SmsCbMessage, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, int); field public static final String ACTION_AREA_INFO_UPDATED = "android.telephony.action.AREA_INFO_UPDATED"; @@ -10985,7 +10964,6 @@ package android.telephony { public final class DataSpecificRegistrationInfo implements android.os.Parcelable { method public int describeContents(); method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo(); - method public boolean isUsingCarrierAggregation(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR; } @@ -11223,19 +11201,6 @@ package android.telephony { field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 } - public final class PinResult implements android.os.Parcelable { - ctor public PinResult(int, int); - method public int describeContents(); - method public int getAttemptsRemaining(); - method @NonNull public static android.telephony.PinResult getDefaultFailedResult(); - method public int getType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PinResult> CREATOR; - field public static final int PIN_RESULT_TYPE_FAILURE = 2; // 0x2 - field public static final int PIN_RESULT_TYPE_INCORRECT = 1; // 0x1 - field public static final int PIN_RESULT_TYPE_SUCCESS = 0; // 0x0 - } - public final class PreciseCallState implements android.os.Parcelable { ctor public PreciseCallState(int, int, int, int, int); method public int describeContents(); @@ -11367,11 +11332,9 @@ package android.telephony { method public void fillInNotifierBundle(@NonNull android.os.Bundle); method public int getDataNetworkType(); method public int getDataRegistrationState(); - method public boolean getDataRoamingFromRegistration(); method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int); - method public int getNrFrequencyRange(); method @NonNull public static android.telephony.ServiceState newFromBundle(@NonNull android.os.Bundle); field public static final int ROAMING_TYPE_DOMESTIC = 2; // 0x2 field public static final int ROAMING_TYPE_INTERNATIONAL = 3; // 0x3 @@ -11623,7 +11586,6 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules(); - method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CdmaEriInformation getCdmaEriInformation(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(); @@ -11678,7 +11640,6 @@ package android.telephony { method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled(); - method public boolean isModemEnabledForSlot(int); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String); @@ -11700,6 +11661,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int); + method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig(); method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings(); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); @@ -11730,15 +11692,13 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoiceActivationState(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void shutdownAllRadios(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPin(String); - method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult supplyPinReportPinResult(@NonNull String); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String); - method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult supplyPukReportPinResult(@NonNull String, @NonNull String); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff(); + method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor); method public void updateServiceLocation(); - method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String); field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED"; field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE"; @@ -11906,23 +11866,6 @@ package android.telephony.cdma { package android.telephony.data { - public class ApnSetting implements android.os.Parcelable { - method @NonNull public static String getApnTypesStringFromBitmask(int); - field public static final String TYPE_ALL_STRING = "*"; - field public static final String TYPE_CBS_STRING = "cbs"; - field public static final String TYPE_DEFAULT_STRING = "default"; - field public static final String TYPE_DUN_STRING = "dun"; - field public static final String TYPE_EMERGENCY_STRING = "emergency"; - field public static final String TYPE_FOTA_STRING = "fota"; - field public static final String TYPE_HIPRI_STRING = "hipri"; - field public static final String TYPE_IA_STRING = "ia"; - field public static final String TYPE_IMS_STRING = "ims"; - field public static final String TYPE_MCX_STRING = "mcx"; - field public static final String TYPE_MMS_STRING = "mms"; - field public static final String TYPE_SUPL_STRING = "supl"; - field public static final String TYPE_XCAP_STRING = "xcap"; - } - public final class DataCallResponse implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.net.LinkAddress> getAddresses(); @@ -12248,7 +12191,7 @@ package android.telephony.ims { method public int getEmergencyServiceCategories(); method @NonNull public java.util.List<java.lang.String> getEmergencyUrns(); method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile(); - method @Nullable public android.os.Bundle getProprietaryCallExtras(); + method @NonNull public android.os.Bundle getProprietaryCallExtras(); method public int getRestrictCause(); method public int getServiceType(); method public static int getVideoStateFromCallType(int); @@ -12706,8 +12649,8 @@ package android.telephony.ims { field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11 field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17 field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16 + field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10 field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15 - field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10 field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21 diff --git a/api/system-removed.txt b/api/system-removed.txt index 07b896905986..23e2499236b3 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -11,6 +11,12 @@ package android.app { public class AppOpsManager { method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable int[]); + method @Deprecated public void setNotedAppOpsCollector(@Nullable android.app.AppOpsManager.AppOpsCollector); + } + + @Deprecated public abstract static class AppOpsManager.AppOpsCollector extends android.app.AppOpsManager.OnOpNotedCallback { + ctor public AppOpsManager.AppOpsCollector(); + method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor(); } public class Notification implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index c95bd09014f1..4f4c82b43e70 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -21,6 +21,7 @@ package android { field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS"; + field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS"; field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG"; field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE"; field public static final String WRITE_OBB = "android.permission.WRITE_OBB"; @@ -427,6 +428,7 @@ package android.app { method public boolean isBlockableSystem(); method public boolean isImportanceLockedByCriticalDeviceFunction(); method public boolean isImportanceLockedByOEM(); + method public void lockFields(int); method public void setBlockableSystem(boolean); method public void setDeleted(boolean); method public void setFgServiceShown(boolean); @@ -434,6 +436,7 @@ package android.app { method public void setImportanceLockedByOEM(boolean); method public void setImportantConversation(boolean); method public void setOriginalImportance(int); + field public static final int USER_LOCKED_SOUND = 32; // 0x20 } public final class NotificationChannelGroup implements android.os.Parcelable { @@ -590,6 +593,14 @@ package android.app.backup { } +package android.app.blob { + + public class BlobStoreManager { + method public void waitForIdle(long) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException; + } + +} + package android.app.prediction { public final class AppPredictionContext implements android.os.Parcelable { @@ -784,6 +795,8 @@ package android.content { } public abstract class ContentResolver { + method @NonNull public static android.net.Uri decodeFromFile(@NonNull java.io.File); + method @NonNull public static java.io.File encodeToFile(@NonNull android.net.Uri); method public static String[] getSyncAdapterPackagesForAuthorityAsUser(String, int); } @@ -1268,7 +1281,7 @@ package android.hardware.lights { public final class LightsManager.LightsSession implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest); } public final class LightsRequest { @@ -1727,7 +1740,7 @@ package android.net { } public class EthernetManager { - method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback); } public static interface EthernetManager.TetheredInterfaceCallback { @@ -2801,9 +2814,9 @@ package android.permission { } public final class PermissionManager { - method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public int getRuntimePermissionsVersion(); + method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion(); method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); - method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); + method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); } public static final class PermissionManager.SplitPermissionInfo { @@ -2919,6 +2932,11 @@ package android.provider { method @NonNull public android.provider.DeviceConfig.Properties.Builder setString(@NonNull String, @Nullable String); } + public final class DocumentsContract { + method public static boolean isManageMode(@NonNull android.net.Uri); + method @NonNull public static android.net.Uri setManageMode(@NonNull android.net.Uri); + } + public final class MediaStore { method @NonNull @WorkerThread public static android.net.Uri scanFile(@NonNull android.content.ContentResolver, @NonNull java.io.File); method @WorkerThread public static void scanVolume(@NonNull android.content.ContentResolver, @NonNull String); @@ -3227,10 +3245,10 @@ package android.service.autofill.augmented { public static final class FillResponse.Builder { ctor public FillResponse.Builder(); method @NonNull public android.service.autofill.augmented.FillResponse build(); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@Nullable java.util.List<android.service.autofill.InlinePresentation>); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>); + method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@NonNull android.os.Bundle); + method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@NonNull android.service.autofill.augmented.FillWindow); + method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@NonNull java.util.List<android.service.autofill.InlineAction>); + method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@NonNull java.util.List<android.service.autofill.Dataset>); } public final class FillWindow implements java.lang.AutoCloseable { @@ -3732,11 +3750,12 @@ package android.telephony { method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); + method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void resetOtaEmergencyNumberDbFilePath(); method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String); method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>); - method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String); + method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor); field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 @@ -3837,7 +3856,7 @@ package android.telephony.ims { method public int getEmergencyServiceCategories(); method @NonNull public java.util.List<java.lang.String> getEmergencyUrns(); method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile(); - method @Nullable public android.os.Bundle getProprietaryCallExtras(); + method @NonNull public android.os.Bundle getProprietaryCallExtras(); method public int getRestrictCause(); method public int getServiceType(); method public static int getVideoStateFromCallType(int); @@ -4292,8 +4311,8 @@ package android.telephony.ims { field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11 field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17 field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16 + field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10 field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15 - field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10 field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21 diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index bd6ca47cb4b8..33bfe512cc10 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -113,8 +113,8 @@ cc_defaults { "libbase", "libcutils", "libprotoutil", - "libstatslog", "libstatsmetadata", + "libstatslog_statsd", "libsysutils", "libutils", ], @@ -171,6 +171,37 @@ cc_library_static { ], } +genrule { + name: "statslog_statsd.h", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util", + out: [ + "statslog_statsd.h", + ], +} + +genrule { + name: "statslog_statsd.cpp", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h", + out: [ + "statslog_statsd.cpp", + ], +} + +cc_library_static { + name: "libstatslog_statsd", + generated_sources: ["statslog_statsd.cpp"], + generated_headers: ["statslog_statsd.h"], + export_generated_headers: ["statslog_statsd.h"], + apex_available: [ + "com.android.os.statsd", + "test_com.android.os.statsd", + ], + shared_libs: [ + "libstatssocket", + ] +} // ========= // statsd @@ -300,6 +331,7 @@ cc_test { static_libs: [ "libgmock", "libplatformprotos", + "libstatslog", //TODO(b/150976524): remove this when the tests no longer hardcode atoms. "libstatssocket_private", ], diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 649c00428028..69b9fc7ad9a0 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -34,7 +34,7 @@ #include "state/StateManager.h" #include "stats_log_util.h" #include "stats_util.h" -#include "statslog.h" +#include "statslog_statsd.h" #include "storage/StorageManager.h" using namespace android; @@ -287,17 +287,17 @@ void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback, int64_t firstId = trainInfo->experimentIds.at(0); auto& ids = trainInfo->experimentIds; switch (trainInfo->status) { - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS: + case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS: if (find(ids.begin(), ids.end(), firstId + 1) == ids.end()) { ids.push_back(firstId + 1); } break; - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED: + case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED: if (find(ids.begin(), ids.end(), firstId + 2) == ids.end()) { ids.push_back(firstId + 2); } break; - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS: + case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS: if (find(ids.begin(), ids.end(), firstId + 3) == ids.end()) { ids.push_back(firstId + 3); } @@ -366,13 +366,13 @@ vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t int64_t firstId = trainInfoOnDisk.experimentIds[0]; auto& ids = trainInfoOnDisk.experimentIds; switch (rollbackTypeIn) { - case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE: + case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE: if (find(ids.begin(), ids.end(), firstId + 4) == ids.end()) { ids.push_back(firstId + 4); } StorageManager::writeTrainInfo(trainInfoOnDisk); break; - case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS: + case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS: if (find(ids.begin(), ids.end(), firstId + 5) == ids.end()) { ids.push_back(firstId + 5); } @@ -405,13 +405,13 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { // Hard-coded logic to update train info on disk and fill in any information // this log event may be missing. - if (event->GetTagId() == android::util::BINARY_PUSH_STATE_CHANGED) { + if (event->GetTagId() == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) { onBinaryPushStateChangedEventLocked(event); } // Hard-coded logic to update experiment ids on disk for certain rollback // types and fill the rollback atom with experiment ids - if (event->GetTagId() == android::util::WATCHDOG_ROLLBACK_OCCURRED) { + if (event->GetTagId() == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) { onWatchdogRollbackOccurredLocked(event); } @@ -429,7 +429,7 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { // Hard-coded logic to update the isolated uid's in the uid-map. // The field numbers need to be currently updated by hand with atoms.proto - if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) { + if (event->GetTagId() == android::os::statsd::util::ISOLATED_UID_CHANGED) { onIsolatedUidChangedEventLocked(*event); } @@ -446,7 +446,7 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { } - if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) { + if (event->GetTagId() != android::os::statsd::util::ISOLATED_UID_CHANGED) { // Map the isolated uid to host uid if necessary. mapIsolatedUidToHostUidIfNecessaryLocked(event); } diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index f18aaa5a9ea0..07579bb21860 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -32,7 +32,7 @@ #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h> #include <frameworks/base/cmds/statsd/src/uid_data.pb.h> #include <private/android_filesystem_config.h> -#include <statslog.h> +#include <statslog_statsd.h> #include <stdio.h> #include <stdlib.h> #include <sys/system_properties.h> @@ -767,7 +767,8 @@ status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& ar } if (good) { dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state); - android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state); + android::os::statsd::util::stats_write( + android::os::statsd::util::APP_BREADCRUMB_REPORTED, uid, label, state); } else { print_cmd_help(out); return UNKNOWN_ERROR; @@ -930,8 +931,6 @@ Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t ve ENFORCE_UID(AID_SYSTEM); VLOG("StatsService::informOnePackage was called"); - // TODO(b/149254662): This is gross. We should consider changing statsd - // internals to use std::string. String16 utf16App = String16(app.c_str()); String16 utf16VersionString = String16(versionString.c_str()); String16 utf16Installer = String16(installer.c_str()); @@ -1190,7 +1189,7 @@ Status StatsService::unsetBroadcastSubscriber(int64_t configId, Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) { // Permission check not necessary as it's meant for applications to write to // statsd. - android::util::stats_write(util::APP_BREADCRUMB_REPORTED, + android::os::statsd::util::stats_write(android::os::statsd::util::APP_BREADCRUMB_REPORTED, (int32_t) AIBinder_getCallingUid(), label, state); return Status::ok(); diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp index 02291181b81b..b632d040eb43 100644 --- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp +++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp @@ -38,8 +38,6 @@ AlarmMonitor::~AlarmMonitor() {} void AlarmMonitor::setStatsCompanionService( shared_ptr<IStatsCompanionService> statsCompanionService) { std::lock_guard<std::mutex> lock(mLock); - // TODO(b/149254662): determine if tmpForLock is needed now that we have moved - // from sp to shared_ptr shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService; mStatsCompanionService = statsCompanionService; if (statsCompanionService == nullptr) { diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp index 019a9f7e5f9a..5722f923d11e 100644 --- a/cmds/statsd/src/anomaly/AlarmTracker.cpp +++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp @@ -23,7 +23,6 @@ #include "stats_util.h" #include "storage/StorageManager.h" -#include <statslog.h> #include <time.h> namespace android { diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index 7ace44eef564..a21abbf042cb 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -25,7 +25,7 @@ #include "subscriber/SubscriberReporter.h" #include <inttypes.h> -#include <statslog.h> +#include <statslog_statsd.h> #include <time.h> namespace android { @@ -235,8 +235,8 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id()); // TODO(b/110564268): This should also take in the const MetricDimensionKey& key? - android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(), - mConfigKey.GetId(), mAlert.id()); + util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(), + mConfigKey.GetId(), mAlert.id()); } void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs, diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 9645a466ec16..32a5243374ff 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -122,10 +122,10 @@ message Atom { SettingChanged setting_changed = 41 [(module) = "framework"]; ActivityForegroundStateChanged activity_foreground_state_changed = 42 [(module) = "framework"]; - IsolatedUidChanged isolated_uid_changed = 43 [(module) = "framework"]; + IsolatedUidChanged isolated_uid_changed = 43 [(module) = "framework", (module) = "statsd"]; PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"]; WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"]; - AnomalyDetected anomaly_detected = 46; + AnomalyDetected anomaly_detected = 46 [(module) = "statsd"]; AppBreadcrumbReported app_breadcrumb_reported = 47 [(allow_from_any_uid) = true, (module) = "statsd"]; AppStartOccurred app_start_occurred = 48 [(module) = "framework"]; @@ -138,7 +138,7 @@ message Atom { AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"]; ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"]; BootSequenceReported boot_sequence_reported = 57; - DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true]; + DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true, (module) = "statsd"]; OverlayStateChanged overlay_state_changed = 59 [(module) = "framework"]; ForegroundServiceStateChanged foreground_service_state_changed = 60 [(module) = "framework"]; @@ -166,7 +166,7 @@ message Atom { LowMemReported low_mem_reported = 81 [(module) = "framework"]; GenericAtom generic_atom = 82; KeyValuePairsAtom key_value_pairs_atom = - 83 [(allow_from_any_uid) = true, (module) = "framework"]; + 83 [(allow_from_any_uid) = true, (module) = "framework", (module) = "statsd"]; VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"]; DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"]; ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true]; @@ -242,7 +242,8 @@ message Atom { AdbConnectionChanged adb_connection_changed = 144 [(module) = "framework"]; SpeechDspStatReported speech_dsp_stat_reported = 145; UsbContaminantReported usb_contaminant_reported = 146 [(module) = "framework"]; - WatchdogRollbackOccurred watchdog_rollback_occurred = 147 [(module) = "framework"]; + WatchdogRollbackOccurred watchdog_rollback_occurred = + 147 [(module) = "framework", (module) = "statsd"]; BiometricSystemHealthIssueDetected biometric_system_health_issue_detected = 148 [(module) = "framework"]; BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"]; @@ -412,7 +413,8 @@ message Atom { SubsystemSleepState subsystem_sleep_state = 10005; CpuTimePerFreq cpu_time_per_freq = 10008 [(module) = "framework"]; CpuTimePerUid cpu_time_per_uid = 10009 [(module) = "framework"]; - CpuTimePerUidFreq cpu_time_per_uid_freq = 10010 [(module) = "framework"]; + CpuTimePerUidFreq cpu_time_per_uid_freq = + 10010 [(module) = "framework", (module) = "statsd"]; WifiActivityInfo wifi_activity_info = 10011 [(module) = "framework"]; ModemActivityInfo modem_activity_info = 10012 [(module) = "framework"]; BluetoothActivityInfo bluetooth_activity_info = 10007 [(module) = "framework"]; @@ -425,9 +427,9 @@ message Atom { RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"]; FullBatteryCapacity full_battery_capacity = 10020 [(module) = "framework"]; Temperature temperature = 10021 [(module) = "framework"]; - BinderCalls binder_calls = 10022 [(module) = "framework"]; + BinderCalls binder_calls = 10022 [(module) = "framework", (module) = "statsd"]; BinderCallsExceptions binder_calls_exceptions = 10023 [(module) = "framework"]; - LooperStats looper_stats = 10024 [(module) = "framework"]; + LooperStats looper_stats = 10024 [(module) = "framework", (module) = "statsd"]; DiskStats disk_stats = 10025 [(module) = "framework"]; DirectoryUsage directory_usage = 10026 [(module) = "framework"]; AppSize app_size = 10027 [(module) = "framework"]; @@ -455,7 +457,7 @@ message Atom { NumFacesEnrolled num_faces_enrolled = 10048 [(module) = "framework"]; RoleHolder role_holder = 10049 [(module) = "framework"]; DangerousPermissionState dangerous_permission_state = 10050 [(module) = "framework"]; - TrainInfo train_info = 10051; + TrainInfo train_info = 10051 [(module) = "statsd"]; TimeZoneDataInfo time_zone_data_info = 10052 [(module) = "framework"]; ExternalStorageInfo external_storage_info = 10053 [(module) = "framework"]; GpuStatsGlobalInfo gpu_stats_global_info = 10054; @@ -7640,6 +7642,9 @@ message AppFeaturesOps { // Whether AppOps is guarded by Runtime permission optional bool is_runtime_permission = 11; + + // Sampling rate used on device, from 0 to 100 + optional int32 sampling_rate = 12; } /** diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 9bdb5881cc94..6d9c644bb40e 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -54,20 +54,22 @@ struct ConfigReceiverDeathCookie { shared_ptr<IPendingIntentRef> mPir; }; -static void configReceiverDied(void* cookie) { +void ConfigManager::configReceiverDied(void* cookie) { auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie); - sp<ConfigManager> configManager = cookie_->mConfigManager; - ConfigKey configKey = cookie_->mConfigKey; - shared_ptr<IPendingIntentRef> pir = cookie_->mPir; - - // TODO(b/149254662): Fix threading. This currently fails if a new - // pir gets set between the get and the remove. - if (configManager->GetConfigReceiver(configKey) == pir) { - configManager->RemoveConfigReceiver(configKey); + sp<ConfigManager>& thiz = cookie_->mConfigManager; + ConfigKey& configKey = cookie_->mConfigKey; + shared_ptr<IPendingIntentRef>& pir = cookie_->mPir; + + // Erase the mapping from the config key to the config receiver (pir) if the + // mapping still exists. + lock_guard<mutex> lock(thiz->mMutex); + auto it = thiz->mConfigReceivers.find(configKey); + if (it != thiz->mConfigReceivers.end() && it->second == pir) { + thiz->mConfigReceivers.erase(configKey); } - // The death recipient corresponding to this specific pir can never - // be triggered again, so free up resources. - // TODO(b/149254662): Investigate other options to manage the memory. + + // The death recipient corresponding to this specific pir can never be + // triggered again, so free up resources. delete cookie_; } @@ -83,26 +85,29 @@ struct ActiveConfigChangedReceiverDeathCookie { shared_ptr<IPendingIntentRef> mPir; }; -static void activeConfigChangedReceiverDied(void* cookie) { +void ConfigManager::activeConfigChangedReceiverDied(void* cookie) { auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie); - sp<ConfigManager> configManager = cookie_->mConfigManager; + sp<ConfigManager>& thiz = cookie_->mConfigManager; int uid = cookie_->mUid; - shared_ptr<IPendingIntentRef> pir = cookie_->mPir; - - // TODO(b/149254662): Fix threading. This currently fails if a new - // pir gets set between the get and the remove. - if (configManager->GetActiveConfigsChangedReceiver(uid) == pir) { - configManager->RemoveActiveConfigsChangedReceiver(uid); + shared_ptr<IPendingIntentRef>& pir = cookie_->mPir; + + // Erase the mapping from the config key to the active config changed + // receiver (pir) if the mapping still exists. + lock_guard<mutex> lock(thiz->mMutex); + auto it = thiz->mActiveConfigsChangedReceivers.find(uid); + if (it != thiz->mActiveConfigsChangedReceivers.end() && it->second == pir) { + thiz->mActiveConfigsChangedReceivers.erase(uid); } + // The death recipient corresponding to this specific pir can never // be triggered again, so free up resources. delete cookie_; } -ConfigManager::ConfigManager(): +ConfigManager::ConfigManager() : mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)), mActiveConfigChangedReceiverDeathRecipient( - AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) { + AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) { } ConfigManager::~ConfigManager() { @@ -189,8 +194,10 @@ void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { void ConfigManager::SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir) { - lock_guard<mutex> lock(mMutex); - mActiveConfigsChangedReceivers[uid] = pir; + { + lock_guard<mutex> lock(mMutex); + mActiveConfigsChangedReceivers[uid] = pir; + } AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(), new ActiveConfigChangedReceiverDeathCookie(this, uid, pir)); } diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h index 824e58849b78..40146b1b2bec 100644 --- a/cmds/statsd/src/config/ConfigManager.h +++ b/cmds/statsd/src/config/ConfigManager.h @@ -161,6 +161,19 @@ private: // IPendingIntentRef dies. ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient; ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient; + + /** + * Death recipient callback that is called when a config receiver dies. + * The cookie is a pointer to a ConfigReceiverDeathCookie. + */ + static void configReceiverDied(void* cookie); + + /** + * Death recipient callback that is called when an active config changed + * receiver dies. The cookie is a pointer to an + * ActiveConfigChangedReceiverDeathCookie. + */ + static void activeConfigChangedReceiverDied(void* cookie); }; } // namespace statsd diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 1c38542b9017..8b6a5a17bc0e 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -32,7 +32,7 @@ #include "../statscompanion_util.h" #include "StatsCallbackPuller.h" #include "TrainInfoPuller.h" -#include "statslog.h" +#include "statslog_statsd.h" using std::shared_ptr; using std::vector; @@ -47,7 +47,7 @@ const int64_t NO_ALARM_UPDATE = INT64_MAX; StatsPullerManager::StatsPullerManager() : kAllPullAtomInfo({ // TrainInfo. - {{.atomTag = android::util::TRAIN_INFO}, new TrainInfoPuller()}, + {{.atomTag = util::TRAIN_INFO}, new TrainInfoPuller()}, }), mNextPullTimeNs(NO_ALARM_UPDATE) { } @@ -85,12 +85,9 @@ void StatsPullerManager::updateAlarmLocked() { return; } - // TODO(b/149254662): Why are we creating a copy here? This is different - // from the other places where we create a copy because we don't reassign - // mStatsCompanionService so a destructor can't implicitly be called... - shared_ptr<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService; - if (statsCompanionServiceCopy != nullptr) { - statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000); + // TODO(b/151045771): do not hold a lock while making a binder call + if (mStatsCompanionService != nullptr) { + mStatsCompanionService->setPullingAlarm(mNextPullTimeNs / 1000000); } else { VLOG("StatsCompanionService not available. Alarm not set."); } @@ -99,8 +96,6 @@ void StatsPullerManager::updateAlarmLocked() { void StatsPullerManager::SetStatsCompanionService( shared_ptr<IStatsCompanionService> statsCompanionService) { - // TODO(b/149254662): Why are we using AutoMutex instead of lock_guard? - // Additionally, do we need the temporary shared_ptr to prevent deadlocks? AutoMutex _l(mLock); shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService; mStatsCompanionService = statsCompanionService; diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp index a7d8d4ebebae..3837f4a1a517 100644 --- a/cmds/statsd/src/external/TrainInfoPuller.cpp +++ b/cmds/statsd/src/external/TrainInfoPuller.cpp @@ -22,7 +22,7 @@ #include "TrainInfoPuller.h" #include "logd/LogEvent.h" #include "stats_log_util.h" -#include "statslog.h" +#include "statslog_statsd.h" #include "storage/StorageManager.h" using std::make_shared; @@ -33,7 +33,7 @@ namespace os { namespace statsd { TrainInfoPuller::TrainInfoPuller() : - StatsPuller(android::util::TRAIN_INFO) { + StatsPuller(util::TRAIN_INFO) { } bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 3054b6d2204b..2bd13d7ad81b 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -20,7 +20,7 @@ #include <android/util/ProtoOutputStream.h> #include "../stats_log_util.h" -#include "statslog.h" +#include "statslog_statsd.h" #include "storage/StorageManager.h" namespace android { @@ -113,9 +113,9 @@ const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID = 1; const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2; const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = { - {android::util::BINDER_CALLS, {6000, 10000}}, - {android::util::LOOPER_STATS, {1500, 2500}}, - {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}}, + {util::BINDER_CALLS, {6000, 10000}}, + {util::LOOPER_STATS, {1500, 2500}}, + {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}}, }; StatsdStats::StatsdStats() { diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 974e203cd612..258f84d0fb79 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -18,7 +18,7 @@ #include "logd/LogEvent.h" #include "stats_log_util.h" -#include "statslog.h" +#include "statslog_statsd.h" #include <android/binder_ibinder.h> #include <android-base/stringprintf.h> @@ -100,7 +100,7 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT const std::map<int32_t, float>& float_map) { mLogdTimestampNs = wallClockTimestampNs; mElapsedTimestampNs = elapsedTimestampNs; - mTagId = android::util::KEY_VALUE_PAIRS_ATOM; + mTagId = util::KEY_VALUE_PAIRS_ATOM; mLogUid = uid; int pos[] = {1, 1, 1}; @@ -153,7 +153,7 @@ LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requi const std::vector<uint8_t>& experimentIds, int32_t userId) { mLogdTimestampNs = getWallClockNs(); mElapsedTimestampNs = getElapsedRealtimeNs(); - mTagId = android::util::BINARY_PUSH_STATE_CHANGED; + mTagId = util::BINARY_PUSH_STATE_CHANGED; mLogUid = AIBinder_getCallingUid(); mLogPid = AIBinder_getCallingPid(); @@ -172,7 +172,7 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const InstallTrainInfo& trainInfo) { mLogdTimestampNs = wallClockTimestampNs; mElapsedTimestampNs = elapsedTimestampNs; - mTagId = android::util::TRAIN_INFO; + mTagId = util::TRAIN_INFO; mValues.push_back( FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode))); diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 3940aa8e1243..b68eeb8d6499 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -328,4 +328,3 @@ void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::v } // namespace statsd } // namespace os } // namespace android - diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 536700f3bfe7..6f54ea7d86c2 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -31,7 +31,7 @@ #include "state/StateManager.h" #include "stats_log_util.h" #include "stats_util.h" -#include "statslog.h" +#include "statslog_statsd.h" using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_INT32; @@ -291,7 +291,7 @@ bool MetricsManager::checkLogCredentials(const LogEvent& event) { } bool MetricsManager::eventSanityCheck(const LogEvent& event) { - if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { + if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) { // Check that app breadcrumb reported fields are valid. status_t err = NO_ERROR; @@ -318,7 +318,7 @@ bool MetricsManager::eventSanityCheck(const LogEvent& event) { VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState); return false; } - } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) { + } else if (event.GetTagId() == util::DAVEY_OCCURRED) { // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp. // Check that the davey duration is reasonable. Max length check is for privacy. status_t err = NO_ERROR; diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 4e3c506e1812..02fe7b1f5003 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -139,7 +139,7 @@ public: // record is deleted. void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set, bool includeVersionStrings, bool includeInstaller, - util::ProtoOutputStream* proto); + ProtoOutputStream* proto); // Forces the output to be cleared. We still generate a snapshot based on the current state. // This results in extra data uploaded but helps us reconstruct the uid mapping on the server diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp index 93af5e937228..c915ef3bf069 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp +++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp @@ -39,33 +39,47 @@ struct BroadcastSubscriberDeathCookie { shared_ptr<IPendingIntentRef> mPir; }; -static void broadcastSubscriberDied(void* cookie) { - BroadcastSubscriberDeathCookie* cookie_ = (BroadcastSubscriberDeathCookie*)cookie; - ConfigKey configKey = cookie_->mConfigKey; +void SubscriberReporter::broadcastSubscriberDied(void* cookie) { + auto cookie_ = static_cast<BroadcastSubscriberDeathCookie*>(cookie); + ConfigKey& configKey = cookie_->mConfigKey; int64_t subscriberId = cookie_->mSubscriberId; - shared_ptr<IPendingIntentRef> pir = cookie_->mPir; - - // TODO(b/149254662): Fix threading. This currently fails if a new pir gets - // set between the get and the unset. - if (SubscriberReporter::getInstance().getBroadcastSubscriber(configKey, subscriberId) == pir) { - SubscriberReporter::getInstance().unsetBroadcastSubscriber(configKey, subscriberId); + shared_ptr<IPendingIntentRef>& pir = cookie_->mPir; + + SubscriberReporter& thiz = getInstance(); + + // Erase the mapping from a (config_key, subscriberId) to a pir if the + // mapping exists. + lock_guard<mutex> lock(thiz.mLock); + auto subscriberMapIt = thiz.mIntentMap.find(configKey); + if (subscriberMapIt != thiz.mIntentMap.end()) { + auto subscriberMap = subscriberMapIt->second; + auto pirIt = subscriberMap.find(subscriberId); + if (pirIt != subscriberMap.end() && pirIt->second == pir) { + subscriberMap.erase(subscriberId); + if (subscriberMap.empty()) { + thiz.mIntentMap.erase(configKey); + } + } } + // The death recipient corresponding to this specific pir can never be // triggered again, so free up resources. delete cookie_; } -static ::ndk::ScopedAIBinder_DeathRecipient sBroadcastSubscriberDeathRecipient( - AIBinder_DeathRecipient_new(broadcastSubscriberDied)); +SubscriberReporter::SubscriberReporter() : + mBroadcastSubscriberDeathRecipient(AIBinder_DeathRecipient_new(broadcastSubscriberDied)) { +} void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId, const shared_ptr<IPendingIntentRef>& pir) { VLOG("SubscriberReporter::setBroadcastSubscriber called."); - lock_guard<mutex> lock(mLock); - mIntentMap[configKey][subscriberId] = pir; - // TODO(b/149254662): Is it ok to call linkToDeath while holding a lock? - AIBinder_linkToDeath(pir->asBinder().get(), sBroadcastSubscriberDeathRecipient.get(), + { + lock_guard<mutex> lock(mLock); + mIntentMap[configKey][subscriberId] = pir; + } + AIBinder_linkToDeath(pir->asBinder().get(), mBroadcastSubscriberDeathRecipient.get(), new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir)); } @@ -103,8 +117,6 @@ void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey, } int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id(); - // TODO(b/149254662): Is there a way to convert a RepeatedPtrField into a - // vector without copying? vector<string> cookies; cookies.reserve(subscription.broadcast_subscriber_details().cookie_size()); for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) { diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h index 0f97d397e331..4fe428198e71 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.h +++ b/cmds/statsd/src/subscriber/SubscriberReporter.h @@ -78,7 +78,7 @@ public: int64_t subscriberId); private: - SubscriberReporter() {}; + SubscriberReporter(); mutable mutex mLock; @@ -94,6 +94,14 @@ private: const Subscription& subscription, const vector<string>& cookies, const MetricDimensionKey& dimKey) const; + + ::ndk::ScopedAIBinder_DeathRecipient mBroadcastSubscriberDeathRecipient; + + /** + * Death recipient callback that is called when a broadcast subscriber dies. + * The cookie is a pointer to a BroadcastSubscriberDeathCookie. + */ + static void broadcastSubscriberDied(void* cookie); }; } // namespace statsd diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 4b78e305f65c..7febb35355a3 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -137,7 +137,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { simplePredicate, trackerNameIndexMap); EXPECT_FALSE(conditionTracker.isSliced()); - LogEvent event(1 /*tagId*/, 0 /*timestamp*/); + // This event is not accessed in this test besides dimensions which is why this is okay. + // This is technically an invalid LogEvent because we do not call parseBuffer. + LogEvent event(/*uid=*/0, /*pid=*/0); vector<MatchingState> matcherState; matcherState.push_back(MatchingState::kNotMatched); @@ -222,7 +224,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { trackerNameIndexMap); EXPECT_FALSE(conditionTracker.isSliced()); - LogEvent event(1 /*tagId*/, 0 /*timestamp*/); + // This event is not accessed in this test besides dimensions which is why this is okay. + // This is technically an invalid LogEvent because we do not call parseBuffer. + LogEvent event(/*uid=*/0, /*pid=*/0); // one matched start vector<MatchingState> matcherState; @@ -296,8 +300,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { std::vector<int> uids = {111, 222, 333}; - LogEvent event(/*uid=*/-1, /*pid=*/-1); - makeWakeLockEvent(&event, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1); + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1); // one matched start vector<MatchingState> matcherState; @@ -308,7 +312,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, + conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, changedCache); if (position == Position::FIRST || position == Position::LAST) { @@ -333,7 +337,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); // another wake lock acquired by this uid - LogEvent event2(/*uid=*/-1, /*pid=*/-1); + LogEvent event2(/*uid=*/0, /*pid=*/0); makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/1); matcherState.clear(); matcherState.push_back(MatchingState::kMatched); @@ -353,7 +357,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { // wake lock 1 release - LogEvent event3(/*uid=*/-1, /*pid=*/-1); + LogEvent event3(/*uid=*/0, /*pid=*/0); makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0); matcherState.clear(); matcherState.push_back(MatchingState::kNotMatched); @@ -372,7 +376,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); - LogEvent event4(/*uid=*/-1, /*pid=*/-1); + LogEvent event4(/*uid=*/0, /*pid=*/0); makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/0); matcherState.clear(); matcherState.push_back(MatchingState::kNotMatched); @@ -399,246 +403,235 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { } -//TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { -// std::vector<sp<ConditionTracker>> allConditions; -// -// SimplePredicate simplePredicate = getWakeLockHeldCondition( -// true /*nesting*/, true /*default to false*/, false /*slice output by uid*/, -// Position::ANY /* position */); -// string conditionName = "WL_HELD"; -// -// unordered_map<int64_t, int> trackerNameIndexMap; -// trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; -// trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; -// trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; -// -// SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), -// 0 /*condition tracker index*/, simplePredicate, -// trackerNameIndexMap); -// -// EXPECT_FALSE(conditionTracker.isSliced()); -// -// std::vector<int> uid_list1 = {111, 1111, 11111}; -// string uid1_wl1 = "wl1_1"; -// std::vector<int> uid_list2 = {222, 2222, 22222}; -// string uid2_wl1 = "wl2_1"; -// -// LogEvent event(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event, uid_list1, uid1_wl1, 1); -// -// // one matched start for uid1 -// vector<MatchingState> matcherState; -// matcherState.push_back(MatchingState::kMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// vector<sp<ConditionTracker>> allPredicates; -// vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); -// vector<bool> changedCache(1, false); -// -// conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, -// changedCache); -// -// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); -// EXPECT_TRUE(changedCache[0]); -// -// // Now test query -// ConditionKey queryKey; -// conditionCache[0] = ConditionState::kNotEvaluated; -// -// conditionTracker.isConditionMet(queryKey, allPredicates, -// true, -// conditionCache); -// EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); -// -// // another wake lock acquired by this uid -// LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event2, uid_list2, uid2_wl1, 1); -// matcherState.clear(); -// matcherState.push_back(MatchingState::kMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, -// changedCache); -// EXPECT_FALSE(changedCache[0]); -// -// // uid1 wake lock 1 release -// LogEvent event3(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event3, uid_list1, uid1_wl1, 0); // now release it. -// matcherState.clear(); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kMatched); -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, -// changedCache); -// // nothing changes, because uid2 is still holding wl. -// EXPECT_FALSE(changedCache[0]); -// -// LogEvent event4(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event4, uid_list2, uid2_wl1, 0); // now release it. -// matcherState.clear(); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kMatched); -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, -// changedCache); -// EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); -// EXPECT_TRUE(changedCache[0]); -// -// // query again -// conditionCache[0] = ConditionState::kNotEvaluated; -// conditionTracker.isConditionMet(queryKey, allPredicates, -// true, -// conditionCache); -// EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); -//} -// -//TEST(SimpleConditionTrackerTest, TestStopAll) { -// std::vector<sp<ConditionTracker>> allConditions; -// for (Position position : -// { Position::FIRST, Position::LAST }) { -// SimplePredicate simplePredicate = getWakeLockHeldCondition( -// true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, -// position); -// string conditionName = "WL_HELD_BY_UID3"; -// -// unordered_map<int64_t, int> trackerNameIndexMap; -// trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; -// trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; -// trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; -// -// SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), -// 0 /*condition tracker index*/, simplePredicate, -// trackerNameIndexMap); -// -// std::vector<int> uid_list1 = {111, 1111, 11111}; -// std::vector<int> uid_list2 = {222, 2222, 22222}; -// -// LogEvent event(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event, uid_list1, "wl1", 1); -// -// // one matched start -// vector<MatchingState> matcherState; -// matcherState.push_back(MatchingState::kMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// vector<sp<ConditionTracker>> allPredicates; -// vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); -// vector<bool> changedCache(1, false); -// -// conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, -// changedCache); -// if (position == Position::FIRST || -// position == Position::LAST) { -// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); -// } else { -// EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size()); -// } -// EXPECT_TRUE(changedCache[0]); -// { -// if (position == Position::FIRST || -// position == Position::LAST) { -// EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); -// } else { -// EXPECT_EQ(uid_list1.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); -// } -// } -// -// // Now test query -// const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName); -// conditionCache[0] = ConditionState::kNotEvaluated; -// -// conditionTracker.isConditionMet(queryKey, allPredicates, -// false, -// conditionCache); -// EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); -// -// // another wake lock acquired by uid2 -// LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); -// makeWakeLockEvent(&event2, uid_list2, "wl2", 1); -// matcherState.clear(); -// matcherState.push_back(MatchingState::kMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, -// changedCache); -// if (position == Position::FIRST || -// position == Position::LAST) { -// EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); -// } else { -// EXPECT_EQ(uid_list1.size() + uid_list2.size(), -// conditionTracker.mSlicedConditionState.size()); -// } -// EXPECT_TRUE(changedCache[0]); -// { -// if (position == Position::FIRST || -// position == Position::LAST) { -// EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); -// } else { -// EXPECT_EQ(uid_list2.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); -// } -// } -// -// -// // TEST QUERY -// const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName); -// conditionCache[0] = ConditionState::kNotEvaluated; -// conditionTracker.isConditionMet(queryKey, allPredicates, -// false, -// conditionCache); -// -// EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); -// -// -// // stop all event -// LogEvent event3(2 /*tagId*/, 0 /*timestamp*/); -// matcherState.clear(); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kNotMatched); -// matcherState.push_back(MatchingState::kMatched); -// -// conditionCache[0] = ConditionState::kNotEvaluated; -// changedCache[0] = false; -// conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, -// changedCache); -// EXPECT_TRUE(changedCache[0]); -// EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); -// { -// if (position == Position::FIRST || position == Position::LAST) { -// EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); -// } else { -// EXPECT_EQ(uid_list1.size() + uid_list2.size(), -// conditionTracker.getChangedToFalseDimensions(allConditions)->size()); -// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); -// } -// } -// -// // TEST QUERY -// const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName); -// conditionCache[0] = ConditionState::kNotEvaluated; -// conditionTracker.isConditionMet(queryKey, allPredicates, -// false, -// conditionCache); -// EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); -// -// // TEST QUERY -// const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName); -// conditionCache[0] = ConditionState::kNotEvaluated; -// conditionTracker.isConditionMet(queryKey, allPredicates, -// false, -// conditionCache); -// EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); -// } -//} +TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { + std::vector<sp<ConditionTracker>> allConditions; + + SimplePredicate simplePredicate = + getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/, + false /*slice output by uid*/, Position::ANY /* position */); + string conditionName = "WL_HELD"; + + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; + trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; + trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; + + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), + 0 /*condition tracker index*/, simplePredicate, + trackerNameIndexMap); + + EXPECT_FALSE(conditionTracker.isSliced()); + + std::vector<int> uids1 = {111, 1111, 11111}; + string uid1_wl1 = "wl1_1"; + std::vector<int> uids2 = {222, 2222, 22222}; + string uid2_wl1 = "wl2_1"; + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, /*acquire=*/1); + + // one matched start for uid1 + vector<MatchingState> matcherState; + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + vector<bool> changedCache(1, false); + + conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, + changedCache); + + EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); + EXPECT_TRUE(changedCache[0]); + + // Now test query + ConditionKey queryKey; + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + // another wake lock acquired by this uid + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, /*acquire=*/1); + + matcherState.clear(); + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_FALSE(changedCache[0]); + + // uid1 wake lock 1 release + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, + /*release=*/0); // now release it. + + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, + changedCache); + // nothing changes, because uid2 is still holding wl. + EXPECT_FALSE(changedCache[0]); + + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, + /*acquire=*/0); // now release it. + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); + EXPECT_TRUE(changedCache[0]); + + // query again + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); +} + +TEST(SimpleConditionTrackerTest, TestStopAll) { + std::vector<sp<ConditionTracker>> allConditions; + for (Position position : {Position::FIRST, Position::LAST}) { + SimplePredicate simplePredicate = + getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/, + true /*output slice by uid*/, position); + string conditionName = "WL_HELD_BY_UID3"; + + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0; + trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1; + trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2; + + SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), + 0 /*condition tracker index*/, simplePredicate, + trackerNameIndexMap); + + std::vector<int> uids1 = {111, 1111, 11111}; + std::vector<int> uids2 = {222, 2222, 22222}; + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, "wl1", /*acquire=*/1); + + // one matched start + vector<MatchingState> matcherState; + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + vector<bool> changedCache(1, false); + + conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache, + changedCache); + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uids1.size(), conditionTracker.mSlicedConditionState.size()); + } + EXPECT_TRUE(changedCache[0]); + { + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uids1.size(), + conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } + } + + // Now test query + const auto queryKey = getWakeLockQueryKey(position, uids1, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + // another wake lock acquired by uid2 + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1); + + matcherState.clear(); + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, + changedCache); + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uids1.size() + uids2.size(), conditionTracker.mSlicedConditionState.size()); + } + EXPECT_TRUE(changedCache[0]); + { + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uids2.size(), + conditionTracker.getChangedToTrueDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty()); + } + } + + // TEST QUERY + const auto queryKey2 = getWakeLockQueryKey(position, uids2, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + // stop all event + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1); + + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_TRUE(changedCache[0]); + EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); + { + if (position == Position::FIRST || position == Position::LAST) { + EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + } else { + EXPECT_EQ(uids1.size() + uids2.size(), + conditionTracker.getChangedToFalseDimensions(allConditions)->size()); + EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty()); + } + } + + // TEST QUERY + const auto queryKey3 = getWakeLockQueryKey(position, uids1, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + + // TEST QUERY + const auto queryKey4 = getWakeLockQueryKey(position, uids2, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + } +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp index 1eaaf08ab2b9..e0eebefd1621 100644 --- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp @@ -53,185 +53,192 @@ StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { -// const int num_buckets = 1; -// const int threshold = 3; -// auto config = CreateStatsdConfig(num_buckets, threshold); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// std::vector<AttributionNodeInternal> attributions2 = { -// CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")}; -// std::vector<AttributionNodeInternal> attributions3 = { -// CreateAttribution(111, "App1"), CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions4 = { -// CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions5 = { -// CreateAttribution(222, "GMSCoreModule1") }; -// -// FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)111)); -// HashableDimensionKey whatKey1({fieldValue1}); -// MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); -// -// FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)222)); -// HashableDimensionKey whatKey2({fieldValue2}); -// MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); -// -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 3); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions3, "wl1", bucketStartTimeNs + 4); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 4); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// // Fired alarm and refractory period end timestamp updated. -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 5); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 100); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + bucketSizeNs + 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 3); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 4); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -//} -// -//TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { -// const int num_buckets = 3; -// const int threshold = 3; -// auto config = CreateStatsdConfig(num_buckets, threshold); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// std::vector<AttributionNodeInternal> attributions2 = { -// CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")}; -// std::vector<AttributionNodeInternal> attributions3 = { -// CreateAttribution(111, "App1"), CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions4 = { -// CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions5 = { -// CreateAttribution(222, "GMSCoreModule1") }; -// -// FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)111)); -// HashableDimensionKey whatKey1({fieldValue1}); -// MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); -// -// FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)222)); -// HashableDimensionKey whatKey2({fieldValue2}); -// MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); -// -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// // Fired alarm and refractory period end timestamp updated. -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 4); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 1); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -// -// event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); -//} +TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { + const int num_buckets = 1; + const int threshold = 3; + auto config = CreateStatsdConfig(num_buckets, threshold); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + std::vector<int> attributionUids1 = {111}; + std::vector<string> attributionTags1 = {"App1"}; + std::vector<int> attributionUids2 = {111, 222}; + std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"}; + std::vector<int> attributionUids3 = {111, 333}; + std::vector<string> attributionTags3 = {"App1", "App3"}; + std::vector<int> attributionUids4 = {222, 333}; + std::vector<string> attributionTags4 = {"GMSCoreModule1", "App3"}; + std::vector<int> attributionUids5 = {222}; + std::vector<string> attributionTags5 = {"GMSCoreModule1"}; + + FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)111)); + HashableDimensionKey whatKey1({fieldValue1}); + MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); + + FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)222)); + HashableDimensionKey whatKey2({fieldValue2}); + MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids4, attributionTags4, + "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids5, attributionTags5, + "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids3, attributionTags3, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids5, attributionTags5, + "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + // Fired alarm and refractory period end timestamp updated. + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 5, attributionUids1, attributionTags1, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 100, attributionUids1, attributionTags1, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids4, + attributionTags4, "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids5, + attributionTags5, "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 3, attributionUids5, + attributionTags5, "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 4, attributionUids5, + attributionTags5, "wl2"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); +} + +TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { + const int num_buckets = 3; + const int threshold = 3; + auto config = CreateStatsdConfig(num_buckets, threshold); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + std::vector<int> attributionUids1 = {111}; + std::vector<string> attributionTags1 = {"App1"}; + std::vector<int> attributionUids2 = {111, 222}; + std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"}; + + FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)111)); + HashableDimensionKey whatKey1({fieldValue1}); + MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Fired alarm and refractory period end timestamp updated. + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids1, attributionTags1, + "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids2, + attributionTags2, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, attributionUids2, + attributionTags2, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 2, attributionUids2, + attributionTags2, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp index 03a209a0e41f..fe45b5507c25 100644 --- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp @@ -69,414 +69,432 @@ StatsdConfig CreateStatsdConfig(int num_buckets, return config; } -} // namespace - -std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), - CreateAttribution(222, "GMSCoreModule1")}; +std::vector<int> attributionUids1 = {111, 222}; +std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"}; -std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"), - CreateAttribution(222, "GMSCoreModule1")}; +std::vector<int> attributionUids2 = {111, 222}; +std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"}; -std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1")}; +std::vector<int> attributionUids3 = {222}; +std::vector<string> attributionTags3 = {"GMSCoreModule1"}; -MetricDimensionKey dimensionKey( - HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED, - (int32_t)0x02010101), Value((int32_t)111))}), - DEFAULT_DIMENSION_KEY); +MetricDimensionKey dimensionKey1( + HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED, + (int32_t)0x02010101), + Value((int32_t)111))}), + DEFAULT_DIMENSION_KEY); MetricDimensionKey dimensionKey2( HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), Value((int32_t)222))}), DEFAULT_DIMENSION_KEY); -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { -// const int num_buckets = 1; -// const uint64_t threshold_ns = NS_PER_SEC; -// auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10 * NS_PER_SEC; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// auto screen_on_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1); -// auto screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 10); -// processor->OnLogEvent(screen_on_event.get()); -// processor->OnLogEvent(screen_off_event.get()); -// -// // Acquire wakelock wl1. -// auto acquire_event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 11); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event. -// auto release_event = CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 101); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock wl1 within bucket #0. -// acquire_event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 110); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock wl1. One anomaly detected. -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + NS_PER_SEC + 109); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock wl1. -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + NS_PER_SEC + 112); -// processor->OnLogEvent(acquire_event.get()); -// // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the -// // end of the refractory period. -// const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey); -// EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, -// (uint32_t)alarmFiredTimestampSec0); -// -// // Anomaly alarm fired. -// auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( -// static_cast<uint32_t>(alarmFiredTimestampSec0)); -// EXPECT_EQ(1u, alarmSet.size()); -// processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock wl1. -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// // Within refractory period. No more anomaly detected. -// EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock wl1. -// acquire_event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11); -// processor->OnLogEvent(acquire_event.get()); -// const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC, -// (uint64_t)alarmFiredTimestampSec1); -// -// // Release wakelock wl1. -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( -// static_cast<uint32_t>(alarmFiredTimestampSec1)); -// EXPECT_EQ(0u, alarmSet.size()); -// -// // Acquire wakelock wl1 near the end of bucket #0. -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 2); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// // Release the event at early bucket #1. -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// // Anomaly detected when stopping the alarm. The refractory period does not change. -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Condition changes to false. -// screen_on_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2 * bucketSizeNs + 20); -// processor->OnLogEvent(screen_on_event.get()); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 30); -// processor->OnLogEvent(acquire_event.get()); -// // The condition is false. Do not start the alarm. -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Condition turns true. -// screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC); -// processor->OnLogEvent(screen_off_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// // Condition turns to false. -// screen_on_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1); -// processor->OnLogEvent(screen_on_event.get()); -// // Condition turns to false. Cancelled the alarm. -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// // Detected one anomaly. -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Condition turns to true again. -// screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2); -// processor->OnLogEvent(screen_off_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -//} -// -//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { -// const int num_buckets = 3; -// const uint64_t threshold_ns = NS_PER_SEC; -// auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10 * NS_PER_SEC; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// auto screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1); -// processor->OnLogEvent(screen_off_event.get()); -// -// // Acquire wakelock "wc1" in bucket #0. -// auto acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock "wc1" in bucket #0. -// auto release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock "wc1" in bucket #1. -// acquire_event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 1); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 100); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire wakelock "wc2" in bucket #2. -// acquire_event = CreateAcquireWakelockEvent( -// attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2, -// anomalyTracker->getAlarmTimestampSec(dimensionKey2)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// // Release wakelock "wc2" in bucket #2. -// release_event = CreateReleaseWakelockEvent( -// attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); -// EXPECT_EQ(refractory_period_sec + -// (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); -// -// // Acquire wakelock "wc1" in bucket #2. -// acquire_event = CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Release wakelock "wc1" in bucket #2. -// release_event = CreateReleaseWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + -// (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4); -// processor->OnLogEvent(acquire_event.get()); -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getAlarmTimestampSec(dimensionKey2)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs + 2); -// processor->OnLogEvent(release_event.get()); -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs + 6); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); -// // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered. -// EXPECT_EQ(refractory_period_sec + -// (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -//} -// -//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { -// const int num_buckets = 2; -// const uint64_t threshold_ns = 3 * NS_PER_SEC; -// auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false); -// int64_t bucketStartTimeNs = 10 * NS_PER_SEC; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; -// -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; -// config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); -// -// sp<AnomalyTracker> anomalyTracker = -// processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; -// -// auto screen_off_event = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1); -// processor->OnLogEvent(screen_off_event.get()); -// -// // Acquire wakelock "wc1" in bucket #0. -// auto acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 100); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Acquire the wakelock "wc1" again. -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1); -// processor->OnLogEvent(acquire_event.get()); -// // The alarm does not change. -// EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // Anomaly alarm fired late. -// const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC; -// auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( -// static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC)); -// EXPECT_EQ(1u, alarmSet.size()); -// processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs - 100); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// auto release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 1); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// // Within the refractory period. No anomaly. -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// // A new wakelock, but still within refractory period. -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC); -// // Still in the refractory period. No anomaly. -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, -// anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// release_event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4); -// processor->OnLogEvent(release_event.get()); -// EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey)); -// -// acquire_event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3); -// processor->OnLogEvent(acquire_event.get()); -// EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, -// anomalyTracker->getAlarmTimestampSec(dimensionKey)); -//} +} // namespace + +TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { + const int num_buckets = 1; + const uint64_t threshold_ns = NS_PER_SEC; + auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10 * NS_PER_SEC; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + auto screen_on_event = CreateScreenStateChangedEvent( + bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + auto screen_off_event = CreateScreenStateChangedEvent( + bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_on_event.get()); + processor->OnLogEvent(screen_off_event.get()); + + // Acquire wakelock wl1. + auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event. + auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock wl1 within bucket #0. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2, + attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock wl1. One anomaly detected. + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock wl1. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the + // end of the refractory period. + const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, + (uint32_t)alarmFiredTimestampSec0); + + // Anomaly alarm fired. + auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( + static_cast<uint32_t>(alarmFiredTimestampSec0)); + EXPECT_EQ(1u, alarmSet.size()); + processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock wl1. + release_event = + CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + // Within refractory period. No more anomaly detected. + EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock wl1. + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC, + (uint64_t)alarmFiredTimestampSec1); + + // Release wakelock wl1. + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + + (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( + static_cast<uint32_t>(alarmFiredTimestampSec1)); + EXPECT_EQ(0u, alarmSet.size()); + + // Acquire wakelock wl1 near the end of bucket #0. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 2, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + // Release the event at early bucket #1. + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + // Anomaly detected when stopping the alarm. The refractory period does not change. + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Condition changes to false. + screen_on_event = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + processor->OnLogEvent(screen_on_event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + // The condition is false. Do not start the alarm. + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Condition turns true. + screen_off_event = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_off_event.get()); + EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + // Condition turns to false. + screen_on_event = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + processor->OnLogEvent(screen_on_event.get()); + // Condition turns to false. Cancelled the alarm. + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + // Detected one anomaly. + EXPECT_EQ(refractory_period_sec + + (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Condition turns to true again. + screen_off_event = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_off_event.get()); + EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(refractory_period_sec + + (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); +} + +TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { + const int num_buckets = 3; + const uint64_t threshold_ns = NS_PER_SEC; + auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10 * NS_PER_SEC; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + auto screen_off_event = CreateScreenStateChangedEvent( + bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_off_event.get()); + + // Acquire wakelock "wc1" in bucket #0. + auto acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock "wc1" in bucket #0. + auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock "wc1" in bucket #1. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 100, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire wakelock "wc2" in bucket #2. + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, + attributionUids3, attributionTags3, "wl2"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2, + anomalyTracker->getAlarmTimestampSec(dimensionKey2)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + // Release wakelock "wc2" in bucket #2. + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC, + attributionUids3, attributionTags3, "wl2"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); + EXPECT_EQ(refractory_period_sec + + (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); + + // Acquire wakelock "wc1" in bucket #2. + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Release wakelock "wc1" in bucket #2. + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC, + attributionUids2, attributionTags2, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + + (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / + NS_PER_SEC + + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4, + attributionUids3, attributionTags3, "wl2"); + processor->OnLogEvent(acquire_event.get()); + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, + anomalyTracker->getAlarmTimestampSec(dimensionKey2)); + + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 2, + attributionUids3, attributionTags3, "wl2"); + processor->OnLogEvent(release_event.get()); + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 6, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); + // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered. + EXPECT_EQ(refractory_period_sec + (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); +} + +TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { + const int num_buckets = 2; + const uint64_t threshold_ns = 3 * NS_PER_SEC; + auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false); + int64_t bucketStartTimeNs = 10 * NS_PER_SEC; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; + + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; + config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + auto screen_off_event = CreateScreenStateChangedEvent( + bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + processor->OnLogEvent(screen_off_event.get()); + + // Acquire wakelock "wc1" in bucket #0. + auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 100, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Acquire the wakelock "wc1" again. + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + // The alarm does not change. + EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // Anomaly alarm fired late. + const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC; + auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( + static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC)); + EXPECT_EQ(1u, alarmSet.size()); + processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + // Within the refractory period. No anomaly. + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + // A new wakelock, but still within refractory period. + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC, + attributionUids1, attributionTags1, "wl1"); + // Still in the refractory period. No anomaly. + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + release_event = + CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(release_event.get()); + EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); + + acquire_event = + CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3, + attributionUids1, attributionTags1, "wl1"); + processor->OnLogEvent(acquire_event.get()); + EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, + anomalyTracker->getAlarmTimestampSec(dimensionKey1)); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp index 605117474014..9e743f7a3157 100644 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp @@ -53,367 +53,318 @@ StatsdConfig CreateStatsdConfig(const Position position) { return config; } +// GMS core node is in the middle. +std::vector<int> attributionUids1 = {111, 222, 333}; +std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "App3"}; + +// GMS core node is the last one. +std::vector<int> attributionUids2 = {111, 333, 222}; +std::vector<string> attributionTags2 = {"App1", "App3", "GMSCoreModule1"}; + +// GMS core node is the first one. +std::vector<int> attributionUids3 = {222, 333}; +std::vector<string> attributionTags3 = {"GMSCoreModule1", "App3"}; + +// Single GMS core node. +std::vector<int> attributionUids4 = {222}; +std::vector<string> attributionTags4 = {"GMSCoreModule1"}; + +// GMS core has another uid. +std::vector<int> attributionUids5 = {111, 444, 333}; +std::vector<string> attributionTags5 = {"App1", "GMSCoreModule2", "App3"}; + +// Multiple GMS core nodes. +std::vector<int> attributionUids6 = {444, 222}; +std::vector<string> attributionTags6 = {"GMSCoreModule2", "GMSCoreModule1"}; + +// No GMS core nodes +std::vector<int> attributionUids7 = {111, 333}; +std::vector<string> attributionTags7 = {"App1", "App3"}; + +std::vector<int> attributionUids8 = {111}; +std::vector<string> attributionTags8 = {"App1"}; + +// GMS core node with isolated uid. +const int isolatedUid = 666; +std::vector<int> attributionUids9 = {isolatedUid}; +std::vector<string> attributionTags9 = {"GMSCoreModule3"}; + +std::vector<int> attributionUids10 = {isolatedUid}; +std::vector<string> attributionTags10 = {"GMSCoreModule1"}; + } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) { -// auto config = CreateStatsdConfig(Position::FIRST); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// // Here it assumes that GMS core has two uids. -// processor->getUidMap()->updateMap( -// 1, {222, 444, 111, 333}, {1, 1, 2, 2}, -// {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, -// {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), -// String16("APP3")}, -// {String16(""), String16(""), String16(""), String16("")}); -// -// // GMS core node is in the middle. -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), -// CreateAttribution(222, "GMSCoreModule1"), -// CreateAttribution(333, "App3")}; -// -// // GMS core node is the last one. -// std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"), -// CreateAttribution(333, "App3"), -// CreateAttribution(222, "GMSCoreModule1")}; -// -// // GMS core node is the first one. -// std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"), -// CreateAttribution(333, "App3")}; -// -// // Single GMS core node. -// std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")}; -// -// // GMS core has another uid. -// std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"), -// CreateAttribution(444, "GMSCoreModule2"), -// CreateAttribution(333, "App3")}; -// -// // Multiple GMS core nodes. -// std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"), -// CreateAttribution(222, "GMSCoreModule1")}; -// -// // No GMS core nodes. -// std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"), -// CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")}; -// -// // GMS core node with isolated uid. -// const int isolatedUid = 666; -// std::vector<AttributionNodeInternal> attributions9 = { -// CreateAttribution(isolatedUid, "GMSCoreModule3")}; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// // Events 1~4 are in the 1st bucket. -// events.push_back(CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 200)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions4, "wl1", bucketStartTimeNs + bucketSizeNs)); -// -// // Events 5~8 are in the 3rd bucket. -// events.push_back(CreateAcquireWakelockEvent( -// attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100)); -// events.push_back(CreateIsolatedUidChangedEvent( -// isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1)); -// events.push_back(CreateIsolatedUidChangedEvent( -// isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(countMetrics.data_size(), 4); -// -// auto data = countMetrics.data(0); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111, -// "App1"); -// EXPECT_EQ(data.bucket_info_size(), 2); -// EXPECT_EQ(data.bucket_info(0).count(), 2); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(data.bucket_info(1).count(), 1); -// EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); -// EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); -// -// data = countMetrics.data(1); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, -// "GMSCoreModule1"); -// EXPECT_EQ(data.bucket_info_size(), 2); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(data.bucket_info(1).count(), 1); -// EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); -// -// data = countMetrics.data(2); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, -// "GMSCoreModule3"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); -// -// data = countMetrics.data(3); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 444, -// "GMSCoreModule2"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); -//} -// -//TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) { -// auto config = CreateStatsdConfig(Position::ALL); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// // Here it assumes that GMS core has two uids. -// processor->getUidMap()->updateMap( -// 1, {222, 444, 111, 333}, {1, 1, 2, 2}, -// {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, -// {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), -// String16("APP3")}, -// {String16(""), String16(""), String16(""), String16("")}); -// -// // GMS core node is in the middle. -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), -// CreateAttribution(222, "GMSCoreModule1"), -// CreateAttribution(333, "App3")}; -// -// // GMS core node is the last one. -// std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"), -// CreateAttribution(333, "App3"), -// CreateAttribution(222, "GMSCoreModule1")}; -// -// // GMS core node is the first one. -// std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"), -// CreateAttribution(333, "App3")}; -// -// // Single GMS core node. -// std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")}; -// -// // GMS core has another uid. -// std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"), -// CreateAttribution(444, "GMSCoreModule2"), -// CreateAttribution(333, "App3")}; -// -// // Multiple GMS core nodes. -// std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"), -// CreateAttribution(222, "GMSCoreModule1")}; -// -// // No GMS core nodes. -// std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"), -// CreateAttribution(333, "App3")}; -// std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")}; -// -// // GMS core node with isolated uid. -// const int isolatedUid = 666; -// std::vector<AttributionNodeInternal> attributions9 = { -// CreateAttribution(isolatedUid, "GMSCoreModule1")}; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// // Events 1~4 are in the 1st bucket. -// events.push_back(CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 2)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions2, "wl1", bucketStartTimeNs + 200)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions4, "wl1", bucketStartTimeNs + bucketSizeNs)); -// -// // Events 5~8 are in the 3rd bucket. -// events.push_back(CreateAcquireWakelockEvent( -// attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1)); -// events.push_back(CreateAcquireWakelockEvent( -// attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100)); -// events.push_back(CreateIsolatedUidChangedEvent( -// isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1)); -// events.push_back(CreateIsolatedUidChangedEvent( -// isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(countMetrics.data_size(), 6); -// -// auto data = countMetrics.data(0); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(1).count()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, -// data.bucket_info(1).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); -// EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); -// -// data = countMetrics.data(2); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(bucketStartTimeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); -// ValidateUidDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(bucketStartTimeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(5); -// ValidateUidDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); -// ValidateUidDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2"); -// ValidateUidDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); -// ValidateAttributionUidAndTagDimension( -// data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); -// EXPECT_EQ(data.bucket_info_size(), 1); -// EXPECT_EQ(data.bucket_info(0).count(), 1); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} +TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) { + auto config = CreateStatsdConfig(Position::FIRST); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + // Here it assumes that GMS core has two uids. + processor->getUidMap()->updateMap( + 1, {222, 444, 111, 333}, {1, 1, 2, 2}, + {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, + {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), + String16("APP3")}, + {String16(""), String16(""), String16(""), String16("")}); + + std::vector<std::unique_ptr<LogEvent>> events; + // Events 1~4 are in the 1st bucket. + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2, + attributionTags2, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, + attributionUids3, attributionTags3, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4, + attributionTags4, "wl1")); + + // Events 5~8 are in the 3rd bucket. + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, + attributionUids5, attributionTags5, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, + attributionUids6, attributionTags6, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2, + attributionUids7, attributionTags7, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs, + attributionUids8, attributionTags8, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, + attributionUids9, attributionTags9, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100, + attributionUids9, attributionTags9, "wl2")); + events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222, + isolatedUid, true /*is_create*/)); + events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222, + isolatedUid, false /*is_create*/)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(countMetrics.data_size(), 4); + + auto data = countMetrics.data(0); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).count(), 2); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).count(), 1); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); + + data = countMetrics.data(1); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).count(), 1); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + + data = countMetrics.data(2); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule3"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); + + data = countMetrics.data(3); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 444, + "GMSCoreModule2"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); +} + +TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) { + auto config = CreateStatsdConfig(Position::ALL); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + // Here it assumes that GMS core has two uids. + processor->getUidMap()->updateMap( + 1, {222, 444, 111, 333}, {1, 1, 2, 2}, + {String16("v1"), String16("v1"), String16("v2"), String16("v2")}, + {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"), + String16("APP3")}, + {String16(""), String16(""), String16(""), String16("")}); + + std::vector<std::unique_ptr<LogEvent>> events; + // Events 1~4 are in the 1st bucket. + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2, + attributionTags2, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, + attributionUids3, attributionTags3, "wl1")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4, + attributionTags4, "wl1")); + + // Events 5~8 are in the 3rd bucket. + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, + attributionUids5, attributionTags5, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, + attributionUids6, attributionTags6, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2, + attributionUids7, attributionTags7, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs, + attributionUids8, attributionTags8, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, + attributionUids10, attributionTags10, "wl2")); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100, + attributionUids10, attributionTags10, "wl2")); + events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222, + isolatedUid, true /*is_create*/)); + events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222, + isolatedUid, false /*is_create*/)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(countMetrics.data_size(), 6); + + auto data = countMetrics.data(0); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(1).count()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + + data = countMetrics.data(2); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 444, + "GMSCoreModule2"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, + android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); + ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, + android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(5); + ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0, + android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1, + android::util::WAKELOCK_STATE_CHANGED, 444, + "GMSCoreModule2"); + ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333); + ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2, + android::util::WAKELOCK_STATE_CHANGED, 333, "App3"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp index f8edee50a3fd..102bb1ea243d 100644 --- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp @@ -56,54 +56,54 @@ StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { } // namespace -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(ConfigTtlE2eTest, TestCountMetric) { -// const int num_buckets = 1; -// const int threshold = 3; -// auto config = CreateStatsdConfig(num_buckets, threshold); -// const uint64_t alert_id = config.alert(0).id(); -// const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); -// -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// -// FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)111)); -// HashableDimensionKey whatKey1({fieldValue1}); -// MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); -// -// FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), -// Value((int32_t)222)); -// HashableDimensionKey whatKey2({fieldValue2}); -// MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); -// -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); -// processor->OnLogEvent(event.get()); -// -// event = CreateAcquireWakelockEvent(attributions1, "wl2", bucketStartTimeNs + bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// -// event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 25 * bucketSizeNs + 2); -// processor->OnLogEvent(event.get()); -// -// EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC), -// processor->mMetricsManagers.begin()->second->getTtlEndNs()); -// -// // Clear the data stored on disk as a result of the ttl. -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true, -// ADB_DUMP, FAST, &buffer); -//} - +TEST(ConfigTtlE2eTest, TestCountMetric) { + const int num_buckets = 1; + const int threshold = 3; + auto config = CreateStatsdConfig(num_buckets, threshold); + const uint64_t alert_id = config.alert(0).id(); + const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + std::vector<int> attributionUids1 = {111}; + std::vector<string> attributionTags1 = {"App1"}; + + FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)111)); + HashableDimensionKey whatKey1({fieldValue1}); + MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); + + FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)222)); + HashableDimensionKey whatKey2({fieldValue2}); + MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY); + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids1, + attributionTags1, "wl2"); + processor->OnLogEvent(event.get()); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * bucketSizeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + + EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC), + processor->mMetricsManagers.begin()->second->getTtlEndNs()); + + // Clear the data stored on disk as a result of the ttl. + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true, + ADB_DUMP, FAST, &buffer); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp index a1f74a631f56..2cd7854420a9 100644 --- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -27,773 +27,775 @@ namespace statsd { #ifdef __ANDROID__ -// TODO(b/149590301): Update these tests to use new socket schema. -///** -// * Test a count metric that has one slice_by_state with no primary fields. -// * -// * Once the CountMetricProducer is initialized, it has one atom id in -// * mSlicedStateAtoms and no entries in mStateGroupMap. -// -// * One StateTracker tracks the state atom, and it has one listener which is the -// * CountMetricProducer that was initialized. -// */ -//TEST(CountMetricE2eTest, TestSlicedState) { -// // Initialize config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto syncStartMatcher = CreateSyncStartAtomMatcher(); -// *config.add_atom_matcher() = syncStartMatcher; -// -// auto state = CreateScreenState(); -// *config.add_state() = state; -// -// // Create count metric that slices by screen state. -// int64_t metricId = 123456; -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(metricId); -// countMetric->set_what(syncStartMatcher.id()); -// countMetric->set_bucket(TimeUnit::FIVE_MINUTES); -// countMetric->add_slice_by_state(state.id()); -// -// // Initialize StatsLogProcessor. -// const uint64_t bucketStartTimeNs = 10000000000; // 0:10 -// const uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// -// // Check that CountMetricProducer was initialized correctly. -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); -// -// // Check that StateTrackers were initialized correctly. -// EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); -// -// /* -// bucket #1 bucket #2 -// | 1 2 3 4 5 6 7 8 9 10 (minutes) -// |-----------------------------|-----------------------------|-- -// x x x x x x (syncStartEvents) -// | | (ScreenIsOnEvent) -// | | (ScreenIsOffEvent) -// | (ScreenUnknownEvent) -// */ -// // Initialize log events - first bucket. -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 50 * NS_PER_SEC)); // 1:00 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 75 * NS_PER_SEC)); // 1:25 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 -// -// // Initialize log events - second bucket. -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 350 * NS_PER_SEC)); // 6:00 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 400 * NS_PER_SEC)); // 6:50 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 450 * NS_PER_SEC)); // 7:40 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 475 * NS_PER_SEC)); // 8:05 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, -// bucketStartTimeNs + 500 * NS_PER_SEC)); // 8:30 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 520 * NS_PER_SEC)); // 8:50 -// -// // Send log events to StatsLogProcessor. -// for (auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// // Check dump report. -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, -// FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); -// EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// // For each CountMetricData, check StateValue info is correct and buckets -// // have correct counts. -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(1, data.bucket_info(1).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(1); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(2); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(2, data.bucket_info(1).count()); -//} -// -///** -// * Test a count metric that has one slice_by_state with a mapping and no -// * primary fields. -// * -// * Once the CountMetricProducer is initialized, it has one atom id in -// * mSlicedStateAtoms and has one entry per state value in mStateGroupMap. -// * -// * One StateTracker tracks the state atom, and it has one listener which is the -// * CountMetricProducer that was initialized. -// */ -//TEST(CountMetricE2eTest, TestSlicedStateWithMap) { -// // Initialize config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto syncStartMatcher = CreateSyncStartAtomMatcher(); -// *config.add_atom_matcher() = syncStartMatcher; -// -// auto state = CreateScreenStateWithOnOffMap(); -// *config.add_state() = state; -// -// // Create count metric that slices by screen state with on/off map. -// int64_t metricId = 123456; -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(metricId); -// countMetric->set_what(syncStartMatcher.id()); -// countMetric->set_bucket(TimeUnit::FIVE_MINUTES); -// countMetric->add_slice_by_state(state.id()); -// -// // Initialize StatsLogProcessor. -// const uint64_t bucketStartTimeNs = 10000000000; // 0:10 -// const uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// -// // Check that StateTrackers were initialized correctly. -// EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); -// -// // Check that CountMetricProducer was initialized correctly. -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); -// -// StateMap map = state.map(); -// for (auto group : map.group()) { -// for (auto value : group.value()) { -// EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], -// group.group_id()); -// } -// } -// -// /* -// bucket #1 bucket #2 -// | 1 2 3 4 5 6 7 8 9 10 (minutes) -// |-----------------------------|-----------------------------|-- -// x x x x x x x x x (syncStartEvents) -// -----------------------------------------------------------SCREEN_OFF events -// | (ScreenStateUnknownEvent = 0) -// | | (ScreenStateOffEvent = 1) -// | (ScreenStateDozeEvent = 3) -// | (ScreenStateDozeSuspendEvent = 4) -// -----------------------------------------------------------SCREEN_ON events -// | | (ScreenStateOnEvent = 2) -// | (ScreenStateVrEvent = 5) -// | (ScreenStateOnSuspendEvent = 6) -// */ -// // Initialize log events - first bucket. -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, -// bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_VR, -// bucketStartTimeNs + 180 * NS_PER_SEC)); // 3:10 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, -// bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 -// -// // Initialize log events - second bucket. -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND, -// bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 -// events.push_back(CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND, -// bucketStartTimeNs + 430 * NS_PER_SEC)); // 7:20 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 -// events.push_back(CreateSyncStartEvent(attributions1, "sync_name", -// bucketStartTimeNs + 570 * NS_PER_SEC)); // 9:40 -// -// // Send log events to StatsLogProcessor. -// for (auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// // Check dump report. -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, -// FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); -// EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// // For each CountMetricData, check StateValue info is correct and buckets -// // have correct counts. -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(1); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(4, data.bucket_info(0).count()); -// EXPECT_EQ(2, data.bucket_info(1).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(2); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(1, data.bucket_info(1).count()); -//} -// -///** -// * Test a count metric that has one slice_by_state with a primary field. -// -// * Once the CountMetricProducer is initialized, it should have one -// * MetricStateLink stored. State querying using a non-empty primary key -// * should also work as intended. -// */ -//TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { -// // Initialize config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto appCrashMatcher = -// CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED); -// *config.add_atom_matcher() = appCrashMatcher; -// -// auto state = CreateUidProcessState(); -// *config.add_state() = state; -// -// // Create count metric that slices by uid process state. -// int64_t metricId = 123456; -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(metricId); -// countMetric->set_what(appCrashMatcher.id()); -// countMetric->set_bucket(TimeUnit::FIVE_MINUTES); -// countMetric->add_slice_by_state(state.id()); -// MetricStateLink* stateLink = countMetric->add_state_link(); -// stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); -// auto fieldsInWhat = stateLink->mutable_fields_in_what(); -// *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /* uid */}); -// auto fieldsInState = stateLink->mutable_fields_in_state(); -// *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); -// -// // Initialize StatsLogProcessor. -// const uint64_t bucketStartTimeNs = 10000000000; // 0:10 -// const uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// -// // Check that StateTrackers were initialized correctly. -// EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); -// -// // Check that CountMetricProducer was initialized correctly. -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); -// EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); -// -// /* -// NOTE: "1" or "2" represents the uid associated with the state/app crash event -// bucket #1 bucket #2 -// | 1 2 3 4 5 6 7 8 9 10 -// |-----------------------------|-----------------------------|-- -// 1 1 1 1 1 2 1 1 2 (AppCrashEvents) -// -----------------------------------------------------------PROCESS STATE events -// 1 2 (ProcessStateTopEvent = 1002) -// 1 1 (ProcessStateForegroundServiceEvent = 1003) -// 2 (ProcessStateImportantBackgroundEvent = 1006) -// 1 1 1 (ProcessStateImportantForegroundEvent = 1005) -// -// Based on the diagram above, an AppCrashEvent querying for process state value would return: -// - StateTracker::kStateUnknown -// - Important foreground -// - Top -// - Important foreground -// - Foreground service -// - Top (both the app crash and state still have matching uid = 2) -// -// - Foreground service -// - Foreground service -// - Important background -// */ -// // Initialize log events - first bucket. -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back( -// CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 -// events.push_back( -// CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, -// bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 -// events.push_back(CreateUidProcessStateChangedEvent( -// 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 -// events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, -// bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 -// -// // Initialize log events - second bucket. -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, -// bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 -// events.push_back(CreateUidProcessStateChangedEvent( -// 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, -// bucketStartTimeNs + 430 * NS_PER_SEC)); // 7:20 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 -// events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, -// bucketStartTimeNs + 570 * NS_PER_SEC)); // 9:40 -// -// // Send log events to StatsLogProcessor. -// for (auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// // Check dump report. -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, -// FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); -// EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// // For each CountMetricData, check StateValue info is correct and buckets -// // have correct counts. -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(1); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(2); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(2, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(3); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(2, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(4); -// EXPECT_EQ(1, data.slice_by_state_size()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(2, data.bucket_info(1).count()); -//} -// -//TEST(CountMetricE2eTest, TestMultipleSlicedStates) { -// // Initialize config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto appCrashMatcher = -// CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED); -// *config.add_atom_matcher() = appCrashMatcher; -// -// auto state1 = CreateScreenStateWithOnOffMap(); -// *config.add_state() = state1; -// auto state2 = CreateUidProcessState(); -// *config.add_state() = state2; -// -// // Create count metric that slices by screen state with on/off map and -// // slices by uid process state. -// int64_t metricId = 123456; -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(metricId); -// countMetric->set_what(appCrashMatcher.id()); -// countMetric->set_bucket(TimeUnit::FIVE_MINUTES); -// countMetric->add_slice_by_state(state1.id()); -// countMetric->add_slice_by_state(state2.id()); -// MetricStateLink* stateLink = countMetric->add_state_link(); -// stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); -// auto fieldsInWhat = stateLink->mutable_fields_in_what(); -// *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /* uid */}); -// auto fieldsInState = stateLink->mutable_fields_in_state(); -// *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); -// -// // Initialize StatsLogProcessor. -// const uint64_t bucketStartTimeNs = 10000000000; // 0:10 -// const uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// -// // Check that StateTrackers were properly initialized. -// EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount()); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); -// EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); -// -// // Check that CountMetricProducer was initialized correctly. -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID); -// EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); -// EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); -// -// StateMap map = state1.map(); -// for (auto group : map.group()) { -// for (auto value : group.value()) { -// EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], -// group.group_id()); -// } -// } -// -// /* -// bucket #1 bucket #2 -// | 1 2 3 4 5 6 7 8 9 10 (minutes) -// |-----------------------------|-----------------------------|-- -// 1 1 1 1 1 2 1 1 2 (AppCrashEvents) -// -----------------------------------------------------------SCREEN_OFF events -// | (ScreenStateUnknownEvent = 0) -// | | (ScreenStateOffEvent = 1) -// | (ScreenStateDozeEvent = 3) -// -----------------------------------------------------------SCREEN_ON events -// | | (ScreenStateOnEvent = 2) -// | (ScreenStateOnSuspendEvent = 6) -// -----------------------------------------------------------PROCESS STATE events -// 1 2 (ProcessStateTopEvent = 1002) -// 1 (ProcessStateForegroundServiceEvent = 1003) -// 2 (ProcessStateImportantBackgroundEvent = 1006) -// 1 1 1 (ProcessStateImportantForegroundEvent = 1005) -// -// Based on the diagram above, Screen State / Process State pairs for each -// AppCrashEvent are: -// - StateTracker::kStateUnknown / important foreground -// - off / important foreground -// - off / Top -// - on / important foreground -// - off / important foreground -// - off / top -// -// - off / important foreground -// - off / foreground service -// - on / important background -// -// */ -// // Initialize log events - first bucket. -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 5 * NS_PER_SEC)); // 0:15 -// events.push_back( -// CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, -// bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 -// events.push_back( -// CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 160 * NS_PER_SEC)); // 2:50 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, -// bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 -// events.push_back(CreateUidProcessStateChangedEvent( -// 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 -// events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, -// bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 -// -// // Initialize log events - second bucket. -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, -// bucketStartTimeNs + 380 * NS_PER_SEC)); // 6:30 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND, -// bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 -// events.push_back(CreateUidProcessStateChangedEvent( -// 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, -// bucketStartTimeNs + 420 * NS_PER_SEC)); // 7:10 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 -// events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, -// bucketStartTimeNs + 450 * NS_PER_SEC)); // 7:40 -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 520 * NS_PER_SEC)); // 8:50 -// events.push_back(CreateUidProcessStateChangedEvent( -// 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, -// bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 -// events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, -// bucketStartTimeNs + 570 * NS_PER_SEC)); // 9:40 -// -// // Send log events to StatsLogProcessor. -// for (auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// // Check dump report. -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, -// FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); -// EXPECT_EQ(6, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// // For each CountMetricData, check StateValue info is correct and buckets -// // have correct counts. -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(1); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_value()); -// EXPECT_EQ(-1, data.slice_by_state(0).value()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(2); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); -// EXPECT_EQ(2, data.bucket_info_size()); -// EXPECT_EQ(2, data.bucket_info(0).count()); -// EXPECT_EQ(1, data.bucket_info(1).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(3); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(4); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// -// data = reports.reports(0).metrics(0).count_metrics().data(5); -// EXPECT_EQ(2, data.slice_by_state_size()); -// EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); -// EXPECT_TRUE(data.slice_by_state(0).has_group_id()); -// EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); -// EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); -// EXPECT_TRUE(data.slice_by_state(1).has_value()); -// EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(2, data.bucket_info(0).count()); -//} +/** +* Test a count metric that has one slice_by_state with no primary fields. +* +* Once the CountMetricProducer is initialized, it has one atom id in +* mSlicedStateAtoms and no entries in mStateGroupMap. + +* One StateTracker tracks the state atom, and it has one listener which is the +* CountMetricProducer that was initialized. +*/ +TEST(CountMetricE2eTest, TestSlicedState) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto syncStartMatcher = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = syncStartMatcher; + + auto state = CreateScreenState(); + *config.add_state() = state; + + // Create count metric that slices by screen state. + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(syncStartMatcher.id()); + countMetric->set_bucket(TimeUnit::FIVE_MINUTES); + countMetric->add_slice_by_state(state.id()); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); + + // Check that StateTrackers were initialized correctly. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + /* + bucket #1 bucket #2 + | 1 2 3 4 5 6 7 8 9 10 (minutes) + |-----------------------------|-----------------------------|-- + x x x x x x (syncStartEvents) + | | (ScreenIsOnEvent) + | | (ScreenIsOffEvent) + | (ScreenUnknownEvent) + */ + // Initialize log events - first bucket. + std::vector<int> attributionUids1 = {123}; + std::vector<string> attributionTags1 = {"App1"}; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 50 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:00 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 75 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 1:25 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 150 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 2:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 200 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:30 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 4:20 + + // Initialize log events - second bucket. + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 350 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 6:00 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 6:50 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 450 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 7:40 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 475 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 8:05 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 500 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 8:30 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 520 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 8:50 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, + data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(2, data.bucket_info(1).count()); +} + +/** + * Test a count metric that has one slice_by_state with a mapping and no + * primary fields. + * + * Once the CountMetricProducer is initialized, it has one atom id in + * mSlicedStateAtoms and has one entry per state value in mStateGroupMap. + * + * One StateTracker tracks the state atom, and it has one listener which is the + * CountMetricProducer that was initialized. + */ +TEST(CountMetricE2eTest, TestSlicedStateWithMap) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto syncStartMatcher = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = syncStartMatcher; + + auto state = CreateScreenStateWithOnOffMap(); + *config.add_state() = state; + + // Create count metric that slices by screen state with on/off map. + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(syncStartMatcher.id()); + countMetric->set_bucket(TimeUnit::FIVE_MINUTES); + countMetric->add_slice_by_state(state.id()); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that StateTrackers were initialized correctly. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + + StateMap map = state.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], + group.group_id()); + } + } + + /* + bucket #1 bucket #2 + | 1 2 3 4 5 6 7 8 9 10 (minutes) + |-----------------------------|-----------------------------|-- + x x x x x x x x x (syncStartEvents) + -----------------------------------------------------------SCREEN_OFF events + | (ScreenStateUnknownEvent = 0) + | | (ScreenStateOffEvent = 1) + | (ScreenStateDozeEvent = 3) + | (ScreenStateDozeSuspendEvent = + 4) + -----------------------------------------------------------SCREEN_ON events + | | (ScreenStateOnEvent = 2) + | (ScreenStateVrEvent = 5) + | (ScreenStateOnSuspendEvent = 6) + */ + // Initialize log events - first bucket. + std::vector<int> attributionUids1 = {123}; + std::vector<string> attributionTags1 = {"App1"}; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 20 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 0:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 0:40 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 1:10 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 90 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 120 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 2:10 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 150 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 180 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:10 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 3:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 210 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 4:20 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 280 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:50 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 285 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 4:55 + + // Initialize log events - second bucket. + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 360 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 6:10 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 390 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 430 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 440 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 7:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 540 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 9:10 + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 570 * NS_PER_SEC, attributionUids1, + attributionTags1, "sync_name")); // 9:40 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(4, data.bucket_info(0).count()); + EXPECT_EQ(2, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); +} + +/** +* Test a count metric that has one slice_by_state with a primary field. + +* Once the CountMetricProducer is initialized, it should have one +* MetricStateLink stored. State querying using a non-empty primary key +* should also work as intended. +*/ +TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED); + *config.add_atom_matcher() = appCrashMatcher; + + auto state = CreateUidProcessState(); + *config.add_state() = state; + + // Create count metric that slices by uid process state. + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(appCrashMatcher.id()); + countMetric->set_bucket(TimeUnit::FIVE_MINUTES); + countMetric->add_slice_by_state(state.id()); + MetricStateLink* stateLink = countMetric->add_state_link(); + stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); + auto fieldsInWhat = stateLink->mutable_fields_in_what(); + *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /*uid*/}); + auto fieldsInState = stateLink->mutable_fields_in_state(); + *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/}); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that StateTrackers were initialized correctly. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); + EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); + + /* + NOTE: "1" or "2" represents the uid associated with the state/app crash event + bucket #1 bucket #2 + | 1 2 3 4 5 6 7 8 9 10 + |------------------------|-------------------------|-- + 1 1 1 1 1 2 1 1 2 (AppCrashEvents) + -----------------------------------------------------PROCESS STATE events + 1 2 (TopEvent = 1002) + 1 1 (ForegroundServiceEvent = 1003) + 2 (ImportantBackgroundEvent = 1006) + 1 1 1 (ImportantForegroundEvent = 1005) + + Based on the diagram above, an AppCrashEvent querying for process state value would return: + - StateTracker::kStateUnknown + - Important foreground + - Top + - Important foreground + - Foreground service + - Top (both the app crash and state still have matching uid = 2) + + - Foreground service + - Foreground service + - Important background + */ + // Initialize log events - first bucket. + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 210 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 3:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55 + + // Initialize log events - second bucket. + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 390 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:40 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 430 * NS_PER_SEC, 2 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:20 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 440 * NS_PER_SEC, 1 /*uid*/)); // 7:30 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(3); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(4); + EXPECT_EQ(1, data.slice_by_state_size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(2, data.bucket_info(1).count()); +} + +TEST(CountMetricE2eTest, TestMultipleSlicedStates) { + // Initialize config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto appCrashMatcher = + CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED); + *config.add_atom_matcher() = appCrashMatcher; + + auto state1 = CreateScreenStateWithOnOffMap(); + *config.add_state() = state1; + auto state2 = CreateUidProcessState(); + *config.add_state() = state2; + + // Create count metric that slices by screen state with on/off map and + // slices by uid process state. + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(appCrashMatcher.id()); + countMetric->set_bucket(TimeUnit::FIVE_MINUTES); + countMetric->add_slice_by_state(state1.id()); + countMetric->add_slice_by_state(state2.id()); + MetricStateLink* stateLink = countMetric->add_state_link(); + stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); + auto fieldsInWhat = stateLink->mutable_fields_in_what(); + *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /*uid*/}); + auto fieldsInState = stateLink->mutable_fields_in_state(); + *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/}); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that StateTrackers were properly initialized. + EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); + + StateMap map = state1.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value], + group.group_id()); + } + } + + /* + bucket #1 bucket #2 + | 1 2 3 4 5 6 7 8 9 10 (minutes) + |------------------------|------------------------|-- + 1 1 1 1 1 2 1 1 2 (AppCrashEvents) + ---------------------------------------------------SCREEN_OFF events + | (ScreenUnknownEvent = 0) + | | (ScreenOffEvent = 1) + | (ScreenDozeEvent = 3) + ---------------------------------------------------SCREEN_ON events + | | (ScreenOnEvent = 2) + | (ScreenOnSuspendEvent = 6) + ---------------------------------------------------PROCESS STATE events + 1 2 (TopEvent = 1002) + 1 (ForegroundServiceEvent = 1003) + 2 (ImportantBackgroundEvent = 1006) + 1 1 1 (ImportantForegroundEvent = 1005) + + Based on the diagram above, Screen State / Process State pairs for each + AppCrashEvent are: + - StateTracker::kStateUnknown / important foreground + - off / important foreground + - off / Top + - on / important foreground + - off / important foreground + - off / top + + - off / important foreground + - off / foreground service + - on / important background + + */ + // Initialize log events - first bucket. + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 5 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:15 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 0:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 90 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 160 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:50 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 210 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55 + + // Initialize log events - second bucket. + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 380 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:30 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 390 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 420 * NS_PER_SEC, 2 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:10 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 440 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 7:30 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 450 * NS_PER_SEC, 1 /*uid*/)); // 7:40 + events.push_back(CreateScreenStateChangedEvent( + bucketStartTimeNs + 520 * NS_PER_SEC, + android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 8:50 + events.push_back(CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10 + events.push_back( + CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40 + + // Send log events to StatsLogProcessor. + for (auto& event : events) { + processor->OnLogEvent(event.get()); + } + + // Check dump report. + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(6, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(-1, data.slice_by_state(0).value()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(3); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(4); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(5); + EXPECT_EQ(2, data.slice_by_state_size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id()); + EXPECT_TRUE(data.slice_by_state(1).has_value()); + EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp index 8eb5f69dcf40..b586b06e0175 100644 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp @@ -26,688 +26,688 @@ namespace statsd { #ifdef __ANDROID__ -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(DurationMetricE2eTest, TestOneBucket) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); -// *config.add_atom_matcher() = screenOnMatcher; -// *config.add_atom_matcher() = screenOffMatcher; -// -// auto durationPredicate = CreateScreenIsOnPredicate(); -// *config.add_predicate() = durationPredicate; -// -// int64_t metricId = 123456; -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(metricId); -// durationMetric->set_what(durationPredicate.id()); -// durationMetric->set_bucket(FIVE_MINUTES); -// durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// -// const int64_t baseTimeNs = 0; // 0:00 -// const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 -// const int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); -// -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// -// std::unique_ptr<LogEvent> event; -// -// // Screen is off at start of bucket. -// event = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01 -// processor->OnLogEvent(event.get()); -// -// // Turn screen on. -// const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); -// processor->OnLogEvent(event.get()); -// -// // Turn off screen 30 seconds after turning on. -// const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs); -// processor->OnLogEvent(event.get()); -// -// event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42 -// processor->OnLogEvent(event.get()); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, -// ADB_DUMP, FAST, &buffer); // 5:01 -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); -// -// const StatsLogReport::DurationMetricDataWrapper& durationMetrics = -// reports.reports(0).metrics(0).duration_metrics(); -// EXPECT_EQ(1, durationMetrics.data_size()); -// -// auto data = durationMetrics.data(0); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos()); -// EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestTwoBuckets) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); -// *config.add_atom_matcher() = screenOnMatcher; -// *config.add_atom_matcher() = screenOffMatcher; -// -// auto durationPredicate = CreateScreenIsOnPredicate(); -// *config.add_predicate() = durationPredicate; -// -// int64_t metricId = 123456; -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(metricId); -// durationMetric->set_what(durationPredicate.id()); -// durationMetric->set_bucket(FIVE_MINUTES); -// durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// -// const int64_t baseTimeNs = 0; // 0:00 -// const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 -// const int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); -// -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// -// std::unique_ptr<LogEvent> event; -// -// // Screen is off at start of bucket. -// event = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01 -// processor->OnLogEvent(event.get()); -// -// // Turn screen on. -// const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); -// processor->OnLogEvent(event.get()); -// -// // Turn off screen 30 seconds after turning on. -// const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs); -// processor->OnLogEvent(event.get()); -// -// event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42 -// processor->OnLogEvent(event.get()); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false, true, -// ADB_DUMP, FAST, &buffer); // 10:01 -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); -// -// const StatsLogReport::DurationMetricDataWrapper& durationMetrics = -// reports.reports(0).metrics(0).duration_metrics(); -// EXPECT_EQ(1, durationMetrics.data_size()); -// -// auto data = durationMetrics.data(0); -// EXPECT_EQ(1, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(0, bucketInfo.bucket_num()); -// EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); -// EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestWithActivation) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); -// auto crashMatcher = CreateProcessCrashAtomMatcher(); -// *config.add_atom_matcher() = screenOnMatcher; -// *config.add_atom_matcher() = screenOffMatcher; -// *config.add_atom_matcher() = crashMatcher; -// -// auto durationPredicate = CreateScreenIsOnPredicate(); -// *config.add_predicate() = durationPredicate; -// -// int64_t metricId = 123456; -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(metricId); -// durationMetric->set_what(durationPredicate.id()); -// durationMetric->set_bucket(FIVE_MINUTES); -// durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// auto metric_activation1 = config.add_metric_activation(); -// metric_activation1->set_metric_id(metricId); -// auto event_activation1 = metric_activation1->add_event_activation(); -// event_activation1->set_atom_matcher_id(crashMatcher.id()); -// event_activation1->set_ttl_seconds(30); // 30 secs. -// -// const int64_t bucketStartTimeNs = 10000000000; -// const int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// vector<int64_t> activeConfigsBroadcast; -// -// int broadcastCount = 0; -// StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, -// bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config); // 0:00 -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap.size(), 1u); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// std::unique_ptr<LogEvent> event; -// -// // Turn screen off. -// event = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * NS_PER_SEC); // 0:02 -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC); -// -// // Turn screen on. -// const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); -// processor.OnLogEvent(event.get(), durationStartNs); -// -// // Activate metric. -// const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10 -// const int64_t activationEndNs = -// activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40 -// event = CreateAppCrashEvent(111, activationStartNs); -// processor.OnLogEvent(event.get(), activationStartNs); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// // Expire activation. -// const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC; -// event = CreateScreenBrightnessChangedEvent(64, expirationNs); // 0:47 -// processor.OnLogEvent(event.get(), expirationNs); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap.size(), 1u); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// // Turn off screen 10 seconds after activation expiration. -// const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs); -// processor.OnLogEvent(event.get(),durationEndNs); -// -// // Turn screen on. -// const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs); -// processor.OnLogEvent(event.get(), duration2StartNs); -// -// // Turn off screen. -// const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, duration2EndNs); -// processor.OnLogEvent(event.get(), duration2EndNs); -// -// // Activate metric. -// const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10 -// const int64_t activation2EndNs = -// activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40 -// event = CreateAppCrashEvent(211, activation2StartNs); -// processor.OnLogEvent(event.get(), activation2StartNs); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, -// ADB_DUMP, FAST, &buffer); // 5:01 -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); -// EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); -// -// const StatsLogReport::DurationMetricDataWrapper& durationMetrics = -// reports.reports(0).metrics(0).duration_metrics(); -// EXPECT_EQ(1, durationMetrics.data_size()); -// -// auto data = durationMetrics.data(0); -// EXPECT_EQ(1, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(0, bucketInfo.bucket_num()); -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestWithCondition) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); -// -// auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); -// *config.add_predicate() = holdingWakelockPredicate; -// -// auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); -// *config.add_predicate() = isInBackgroundPredicate; -// -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(StringToId("WakelockDuration")); -// durationMetric->set_what(holdingWakelockPredicate.id()); -// durationMetric->set_condition(isInBackgroundPredicate.id()); -// durationMetric->set_aggregation_type(DurationMetric::SUM); -// durationMetric->set_bucket(FIVE_MINUTES); -// -// ConfigKey cfgKey; -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_TRUE(eventActivationMap.empty()); -// -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// -// auto event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10 -// processor->OnLogEvent(event.get()); -// -// event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22 -// processor->OnLogEvent(event.get()); -// -// event = CreateMoveToForegroundEvent( -// appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15 -// processor->OnLogEvent(event.get()); -// -// event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 4 * 60 * NS_PER_SEC); // 4:00 -// processor->OnLogEvent(event.get()); -// -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); -// -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// -// // Validate bucket info. -// EXPECT_EQ(1, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestWithSlicedCondition) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); -// -// auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); -// // The predicate is dimensioning by first attribution node by uid. -// FieldMatcher dimensions = CreateAttributionUidDimensions( -// android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); -// *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; -// *config.add_predicate() = holdingWakelockPredicate; -// -// auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); -// *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = -// CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); -// *config.add_predicate() = isInBackgroundPredicate; -// -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(StringToId("WakelockDuration")); -// durationMetric->set_what(holdingWakelockPredicate.id()); -// durationMetric->set_condition(isInBackgroundPredicate.id()); -// durationMetric->set_aggregation_type(DurationMetric::SUM); -// // The metric is dimensioning by first attribution node and only by uid. -// *durationMetric->mutable_dimensions_in_what() = -// CreateAttributionUidDimensions( -// android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); -// durationMetric->set_bucket(FIVE_MINUTES); -// -// // Links between wakelock state atom and condition of app is in background. -// auto links = durationMetric->add_links(); -// links->set_condition(isInBackgroundPredicate.id()); -// auto dimensionWhat = links->mutable_fields_in_what(); -// dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED); -// dimensionWhat->add_child()->set_field(1); // uid field. -// *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( -// android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST }); -// -// ConfigKey cfgKey; -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_TRUE(eventActivationMap.empty()); -// -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// -// auto event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10 -// processor->OnLogEvent(event.get()); -// -// event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22 -// processor->OnLogEvent(event.get()); -// -// event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 60 * NS_PER_SEC); // 1:00 -// processor->OnLogEvent(event.get()); -// -// -// event = CreateMoveToForegroundEvent( -// appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15 -// processor->OnLogEvent(event.get()); -// -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); -// -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, appUid); -// // Validate bucket info. -// EXPECT_EQ(1, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos()); -//} -// -//TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) { -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); -// *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); -// *config.add_atom_matcher() = screenOnMatcher; -// -// auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); -// // The predicate is dimensioning by first attribution node by uid. -// FieldMatcher dimensions = CreateAttributionUidDimensions( -// android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); -// *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; -// *config.add_predicate() = holdingWakelockPredicate; -// -// auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); -// *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = -// CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); -// *config.add_predicate() = isInBackgroundPredicate; -// -// auto durationMetric = config.add_duration_metric(); -// durationMetric->set_id(StringToId("WakelockDuration")); -// durationMetric->set_what(holdingWakelockPredicate.id()); -// durationMetric->set_condition(isInBackgroundPredicate.id()); -// durationMetric->set_aggregation_type(DurationMetric::SUM); -// // The metric is dimensioning by first attribution node and only by uid. -// *durationMetric->mutable_dimensions_in_what() = -// CreateAttributionUidDimensions( -// android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); -// durationMetric->set_bucket(FIVE_MINUTES); -// -// // Links between wakelock state atom and condition of app is in background. -// auto links = durationMetric->add_links(); -// links->set_condition(isInBackgroundPredicate.id()); -// auto dimensionWhat = links->mutable_fields_in_what(); -// dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED); -// dimensionWhat->add_child()->set_field(1); // uid field. -// *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( -// android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST }); -// -// auto metric_activation1 = config.add_metric_activation(); -// metric_activation1->set_metric_id(durationMetric->id()); -// auto event_activation1 = metric_activation1->add_event_activation(); -// event_activation1->set_atom_matcher_id(screenOnMatcher.id()); -// event_activation1->set_ttl_seconds(60 * 2); // 2 minutes. -// -// ConfigKey cfgKey; -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap.size(), 1u); -// EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// int appUid = 123; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; -// -// auto event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10 -// processor->OnLogEvent(event.get()); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22 -// processor->OnLogEvent(event.get()); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs); -// processor->OnLogEvent(event.get()); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// const int64_t durationEndNs = -// durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00 -// event = CreateAppCrashEvent(333, durationEndNs); -// processor->OnLogEvent(event.get()); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// event = CreateMoveToForegroundEvent( -// appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15 -// processor->OnLogEvent(event.get()); -// -// event = CreateReleaseWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC); // 4:17 -// processor->OnLogEvent(event.get()); -// -// event = CreateMoveToBackgroundEvent( -// appUid, bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC); // 4:20 -// processor->OnLogEvent(event.get()); -// -// event = CreateAcquireWakelockEvent( -// attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC); // 4:25 -// processor->OnLogEvent(event.get()); -// -// const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30 -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs); -// processor->OnLogEvent(event.get()); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs); -// EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); -// -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_GT(buffer.size(), 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); -// -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, appUid); -// // Validate bucket info. -// EXPECT_EQ(2, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); -// -// bucketInfo = data.bucket_info(1); -// EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos()); -//} +TEST(DurationMetricE2eTest, TestOneBucket) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + *config.add_atom_matcher() = screenOffMatcher; + + auto durationPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = durationPredicate; + + int64_t metricId = 123456; + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(metricId); + durationMetric->set_what(durationPredicate.id()); + durationMetric->set_bucket(FIVE_MINUTES); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); + + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + + std::unique_ptr<LogEvent> event; + + // Screen is off at start of bucket. + event = CreateScreenStateChangedEvent(configAddedTimeNs, + android::view::DISPLAY_STATE_OFF); // 0:01 + processor->OnLogEvent(event.get()); + + // Turn screen on. + const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 + event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(event.get()); + + // Turn off screen 30 seconds after turning on. + const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 + event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(event.get()); + + event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42 + processor->OnLogEvent(event.get()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, + ADB_DUMP, FAST, &buffer); // 5:01 + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + const StatsLogReport::DurationMetricDataWrapper& durationMetrics = + reports.reports(0).metrics(0).duration_metrics(); + EXPECT_EQ(1, durationMetrics.data_size()); + + auto data = durationMetrics.data(0); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos()); + EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(DurationMetricE2eTest, TestTwoBuckets) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + *config.add_atom_matcher() = screenOffMatcher; + + auto durationPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = durationPredicate; + + int64_t metricId = 123456; + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(metricId); + durationMetric->set_what(durationPredicate.id()); + durationMetric->set_bucket(FIVE_MINUTES); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); + + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + + std::unique_ptr<LogEvent> event; + + // Screen is off at start of bucket. + event = CreateScreenStateChangedEvent(configAddedTimeNs, + android::view::DISPLAY_STATE_OFF); // 0:01 + processor->OnLogEvent(event.get()); + + // Turn screen on. + const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11 + event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(event.get()); + + // Turn off screen 30 seconds after turning on. + const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41 + event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(event.get()); + + event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42 + processor->OnLogEvent(event.get()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false, + true, ADB_DUMP, FAST, &buffer); // 10:01 + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + const StatsLogReport::DurationMetricDataWrapper& durationMetrics = + reports.reports(0).metrics(0).duration_metrics(); + EXPECT_EQ(1, durationMetrics.data_size()); + + auto data = durationMetrics.data(0); + EXPECT_EQ(1, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(0, bucketInfo.bucket_num()); + EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); + EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); +} + +TEST(DurationMetricE2eTest, TestWithActivation) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher(); + auto crashMatcher = CreateProcessCrashAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + *config.add_atom_matcher() = screenOffMatcher; + *config.add_atom_matcher() = crashMatcher; + + auto durationPredicate = CreateScreenIsOnPredicate(); + *config.add_predicate() = durationPredicate; + + int64_t metricId = 123456; + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(metricId); + durationMetric->set_what(durationPredicate.id()); + durationMetric->set_bucket(FIVE_MINUTES); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); + + auto metric_activation1 = config.add_metric_activation(); + metric_activation1->set_metric_id(metricId); + auto event_activation1 = metric_activation1->add_event_activation(); + event_activation1->set_atom_matcher_id(crashMatcher.id()); + event_activation1->set_ttl_seconds(30); // 30 secs. + + const int64_t bucketStartTimeNs = 10000000000; + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + int broadcastCount = 0; + StatsLogProcessor processor( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs, + [](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(bucketStartTimeNs, cfgKey, config); // 0:00 + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap.size(), 1u); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + std::unique_ptr<LogEvent> event; + + // Turn screen off. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * NS_PER_SEC, + android::view::DISPLAY_STATE_OFF); // 0:02 + processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC); + + // Turn screen on. + const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05 + event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), durationStartNs); + + // Activate metric. + const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10 + const int64_t activationEndNs = + activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40 + event = CreateAppCrashEvent(activationStartNs, 111); + processor.OnLogEvent(event.get(), activationStartNs); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + // Expire activation. + const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC; + event = CreateScreenBrightnessChangedEvent(expirationNs, 64); // 0:47 + processor.OnLogEvent(event.get(), expirationNs); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap.size(), 1u); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + // Turn off screen 10 seconds after activation expiration. + const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50 + event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF); + processor.OnLogEvent(event.get(), durationEndNs); + + // Turn screen on. + const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55 + event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), duration2StartNs); + + // Turn off screen. + const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05 + event = CreateScreenStateChangedEvent(duration2EndNs, android::view::DISPLAY_STATE_OFF); + processor.OnLogEvent(event.get(), duration2EndNs); + + // Activate metric. + const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10 + const int64_t activation2EndNs = + activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40 + event = CreateAppCrashEvent(activation2StartNs, 211); + processor.OnLogEvent(event.get(), activation2StartNs); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true, + ADB_DUMP, FAST, &buffer); // 5:01 + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id()); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + const StatsLogReport::DurationMetricDataWrapper& durationMetrics = + reports.reports(0).metrics(0).duration_metrics(); + EXPECT_EQ(1, durationMetrics.data_size()); + + auto data = durationMetrics.data(0); + EXPECT_EQ(1, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(0, bucketInfo.bucket_num()); + EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos()); +} + +TEST(DurationMetricE2eTest, TestWithCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + *config.add_predicate() = holdingWakelockPredicate; + + auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *config.add_predicate() = isInBackgroundPredicate; + + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + durationMetric->set_bucket(FIVE_MINUTES); + + ConfigKey cfgKey; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_TRUE(eventActivationMap.empty()); + + int appUid = 123; + vector<int> attributionUids1 = {appUid}; + vector<string> attributionTags1 = {"App1"}; + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 + processor->OnLogEvent(event.get()); + + event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, + appUid); // 3:15 + processor->OnLogEvent(event.get()); + + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 4 * 60 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 4:00 + processor->OnLogEvent(event.get()); + + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + + // Validate bucket info. + EXPECT_EQ(1, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos()); +} + +TEST(DurationMetricE2eTest, TestWithSlicedCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + FieldMatcher dimensions = CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED, + {Position::FIRST}); + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; + *config.add_predicate() = holdingWakelockPredicate; + + auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = isInBackgroundPredicate; + + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + + // Links between wakelock state atom and condition of app is in background. + auto links = durationMetric->add_links(); + links->set_condition(isInBackgroundPredicate.id()); + auto dimensionWhat = links->mutable_fields_in_what(); + dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED); + dimensionWhat->add_child()->set_field(1); // uid field. + *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( + android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + + ConfigKey cfgKey; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_TRUE(eventActivationMap.empty()); + + int appUid = 123; + std::vector<int> attributionUids1 = {appUid}; + std::vector<string> attributionTags1 = {"App1"}; + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 + processor->OnLogEvent(event.get()); + + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 1:00 + processor->OnLogEvent(event.get()); + + event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, + appUid); // 3:15 + processor->OnLogEvent(event.get()); + + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, appUid); + // Validate bucket info. + EXPECT_EQ(1, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos()); +} + +TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + *config.add_atom_matcher() = screenOnMatcher; + + auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + FieldMatcher dimensions = CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED, + {Position::FIRST}); + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; + *config.add_predicate() = holdingWakelockPredicate; + + auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = isInBackgroundPredicate; + + auto durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( + android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + + // Links between wakelock state atom and condition of app is in background. + auto links = durationMetric->add_links(); + links->set_condition(isInBackgroundPredicate.id()); + auto dimensionWhat = links->mutable_fields_in_what(); + dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED); + dimensionWhat->add_child()->set_field(1); // uid field. + *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( + android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + + auto metric_activation1 = config.add_metric_activation(); + metric_activation1->set_metric_id(durationMetric->id()); + auto event_activation1 = metric_activation1->add_event_activation(); + event_activation1->set_atom_matcher_id(screenOnMatcher.id()); + event_activation1->set_ttl_seconds(60 * 2); // 2 minutes. + + ConfigKey cfgKey; + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap.size(), 1u); + EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, 0); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + int appUid = 123; + std::vector<int> attributionUids1 = {appUid}; + std::vector<string> attributionTags1 = {"App1"}; + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, 0); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22 + processor->OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, 0); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30 + event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + const int64_t durationEndNs = + durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00 + event = CreateAppCrashEvent(durationEndNs, 333); + processor->OnLogEvent(event.get()); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, + appUid); // 3:15 + processor->OnLogEvent(event.get()); + + event = CreateReleaseWakelockEvent(bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC, + attributionUids1, attributionTags1, "wl1"); // 4:17 + processor->OnLogEvent(event.get()); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC, + appUid); // 4:20 + processor->OnLogEvent(event.get()); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC, + attributionUids1, attributionTags1, "wl1"); // 4:25 + processor->OnLogEvent(event.get()); + + const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30 + event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(event.get()); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs); + EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC); + + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_GT(buffer.size(), 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size()); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, appUid); + // Validate bucket info. + EXPECT_EQ(2, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos()); + + bucketInfo = data.bucket_info(1); + EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos()); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp index 7f651d4ba529..594c1e6bf6e7 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp @@ -65,482 +65,465 @@ StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type, } // namespaces -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { -// auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// ATOM_TAG); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// // When creating the config, the gauge metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& nextPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); -// -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// // Pulling alarm arrives on time and reset the sequential pulling alarm. -// processor->informPullAlarmFired(nextPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + bucketSizeNs + 10); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + bucketSizeNs + 100); -// processor->OnLogEvent(screenOffEvent.get()); -// -// processor->informPullAlarmFired(nextPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, -// nextPullTimeNs); -// -// processor->informPullAlarmFired(nextPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); -// -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 3 * bucketSizeNs + 2); -// processor->OnLogEvent(screenOnEvent.get()); -// -// processor->informPullAlarmFired(nextPullTimeNs + 3); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 5 * bucketSizeNs + 1); -// processor->OnLogEvent(screenOffEvent.get()); -// -// processor->informPullAlarmFired(nextPullTimeNs + 2); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs); -// -// processor->informPullAlarmFired(nextPullTimeNs + 2); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_GT((int)gaugeMetrics.data_size(), 1); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(6, data.bucket_info_size()); -// -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, -// data.bucket_info(1).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(2).atom_size()); -// EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, -// data.bucket_info(2).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(3).atom_size()); -// EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1, -// data.bucket_info(3).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(4).atom_size()); -// EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, -// data.bucket_info(4).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(5).atom_size()); -// EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2, -// data.bucket_info(5).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0); -//} -// -//TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) { -// auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// ATOM_TAG); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + bucketSizeNs + 10); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + bucketSizeNs + 100); -// processor->OnLogEvent(screenOffEvent.get()); -// -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 3 * bucketSizeNs + 2); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 5 * bucketSizeNs + 1); -// processor->OnLogEvent(screenOffEvent.get()); -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 5 * bucketSizeNs + 3); -// processor->OnLogEvent(screenOnEvent.get()); -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 5 * bucketSizeNs + 10); -// processor->OnLogEvent(screenOffEvent.get()); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_GT((int)gaugeMetrics.data_size(), 1); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(3, data.bucket_info_size()); -// -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100, -// data.bucket_info(1).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(2, data.bucket_info(2).atom_size()); -// EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, -// data.bucket_info(2).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10, -// data.bucket_info(2).elapsed_timestamp_nanos(1)); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); -// EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0); -//} -// -// -//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { -// auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// ATOM_TAG); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// // When creating the config, the gauge metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& nextPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); -// -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + bucketSizeNs + 10); -// processor->OnLogEvent(screenOnEvent.get()); -// -// // Pulling alarm arrives one bucket size late. -// processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 3 * bucketSizeNs + 11); -// processor->OnLogEvent(screenOffEvent.get()); -// -// // Pulling alarm arrives more than one bucket size late. -// processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_GT((int)gaugeMetrics.data_size(), 1); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(3, data.bucket_info_size()); -// -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11, -// data.bucket_info(1).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); -// -// EXPECT_EQ(1, data.bucket_info(2).atom_size()); -// EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12, -// data.bucket_info(2).elapsed_timestamp_nanos(0)); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); -//} -// -//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) { -// auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); -// -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); -// *config.add_atom_matcher() = batterySaverStartMatcher; -// const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. -// auto metric_activation = config.add_metric_activation(); -// metric_activation->set_metric_id(metricId); -// metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); -// auto event_activation = metric_activation->add_event_activation(); -// event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); -// event_activation->set_ttl_seconds(ttlNs / 1000000000); -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// ATOM_TAG); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // When creating the config, the gauge metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& nextPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); -// -// // Pulling alarm arrives on time and reset the sequential pulling alarm. -// // Event should not be kept. -// processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // Activate the metric. A pull occurs upon activation. -// const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. -// auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); -// processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // This event should be kept. 2 total. -// processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, -// nextPullTimeNs); -// -// // This event should be kept. 3 total. -// processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); -// -// // Create random event to deactivate metric. -// auto deactivationEvent = CreateScreenBrightnessChangedEvent(50, activationNs + ttlNs + 1); -// processor->OnLogEvent(deactivationEvent.get()); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // Event should not be kept. 3 total. -// processor->informPullAlarmFired(nextPullTimeNs + 3); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); -// -// processor->informPullAlarmFired(nextPullTimeNs + 2); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_GT((int)gaugeMetrics.data_size(), 0); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(3, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(1, bucketInfo.atom_size()); -// EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); -// EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); -// -// bucketInfo = data.bucket_info(1); -// EXPECT_EQ(1, bucketInfo.atom_size()); -// EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); -// -// bucketInfo = data.bucket_info(2); -// EXPECT_EQ(1, bucketInfo.atom_size()); -// EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0)); -// EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)), -// bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)), -// bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); -// EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); -//} +TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { + auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + // When creating the config, the gauge metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& nextPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + // Pulling alarm arrives on time and reset the sequential pulling alarm. + processor->informPullAlarmFired(nextPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); + + auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + processor->informPullAlarmFired(nextPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); + + processor->informPullAlarmFired(nextPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); + + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + processor->informPullAlarmFired(nextPullTimeNs + 3); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + processor->informPullAlarmFired(nextPullTimeNs + 2); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs); + + processor->informPullAlarmFired(nextPullTimeNs + 2); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + EXPECT_GT((int)gaugeMetrics.data_size(), 1); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(6, data.bucket_info_size()); + + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(2).atom_size()); + EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(3).atom_size()); + EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1, data.bucket_info(3).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(4).atom_size()); + EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(4).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(5).atom_size()); + EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2, data.bucket_info(5).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0); +} + +TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) { + auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 3, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 10, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + EXPECT_GT((int)gaugeMetrics.data_size(), 1); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(3, data.bucket_info_size()); + + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100, data.bucket_info(1).elapsed_timestamp_nanos(0)); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(2, data.bucket_info(2).atom_size()); + EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10, data.bucket_info(2).elapsed_timestamp_nanos(1)); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); + EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0); +} + +TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { + auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + // When creating the config, the gauge metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& nextPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + // Pulling alarm arrives one bucket size late. + processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 11, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + // Pulling alarm arrives more than one bucket size late. + processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + EXPECT_GT((int)gaugeMetrics.data_size(), 1); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(3, data.bucket_info_size()); + + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11, + data.bucket_info(1).elapsed_timestamp_nanos(0)); + EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0); + + EXPECT_EQ(1, data.bucket_info(2).atom_size()); + EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12, data.bucket_info(2).elapsed_timestamp_nanos(0)); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0); +} + +TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) { + auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); + + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); + *config.add_atom_matcher() = batterySaverStartMatcher; + const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. + auto metric_activation = config.add_metric_activation(); + metric_activation->set_metric_id(metricId); + metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto event_activation = metric_activation->add_event_activation(); + event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); + event_activation->set_ttl_seconds(ttlNs / 1000000000); + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // When creating the config, the gauge metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& nextPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs); + + // Pulling alarm arrives on time and reset the sequential pulling alarm. + // Event should not be kept. + processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // Activate the metric. A pull occurs upon activation. + const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. + auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); + processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // This event should be kept. 2 total. + processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs); + + // This event should be kept. 3 total. + processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs); + + // Create random event to deactivate metric. + auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50); + processor->OnLogEvent(deactivationEvent.get()); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // Event should not be kept. 3 total. + processor->informPullAlarmFired(nextPullTimeNs + 3); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs); + + processor->informPullAlarmFired(nextPullTimeNs + 2); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); + EXPECT_GT((int)gaugeMetrics.data_size(), 0); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(3, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(1, bucketInfo.atom_size()); + EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); + EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); + + bucketInfo = data.bucket_info(1); + EXPECT_EQ(1, bucketInfo.atom_size()); + EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); + + bucketInfo = data.bucket_info(2); + EXPECT_EQ(1, bucketInfo.atom_size()); + EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0)); + EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size()); + EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)), + bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)), + bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty()); + EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0); +} TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition) { auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false); diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp index ef6e753b802d..6e3d93a5547f 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp @@ -14,12 +14,13 @@ #include <gtest/gtest.h> +#include <vector> + #include "src/StatsLogProcessor.h" #include "src/stats_log_util.h" +#include "stats_event.h" #include "tests/statsd_test_util.h" -#include <vector> - namespace android { namespace os { namespace statsd { @@ -68,221 +69,227 @@ StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sa return config; } -// TODO(b/149590301): Update this helper to use new socket schema. -//std::unique_ptr<LogEvent> CreateAppStartOccurredEvent( -// const int uid, const string& pkg_name, AppStartOccurred::TransitionType type, -// const string& activity_name, const string& calling_pkg_name, const bool is_instant_app, -// int64_t activity_start_msec, uint64_t timestampNs) { -// auto logEvent = std::make_unique<LogEvent>( -// android::util::APP_START_OCCURRED, timestampNs); -// logEvent->write(uid); -// logEvent->write(pkg_name); -// logEvent->write(type); -// logEvent->write(activity_name); -// logEvent->write(calling_pkg_name); -// logEvent->write(is_instant_app); -// logEvent->write(activity_start_msec); -// logEvent->init(); -// return logEvent; -//} +std::unique_ptr<LogEvent> CreateAppStartOccurredEvent( + uint64_t timestampNs, const int uid, const string& pkg_name, + AppStartOccurred::TransitionType type, const string& activity_name, + const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::APP_START_OCCURRED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, pkg_name.c_str()); + AStatsEvent_writeInt32(statsEvent, type); + AStatsEvent_writeString(statsEvent, activity_name.c_str()); + AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str()); + AStatsEvent_writeInt32(statsEvent, is_instant_app); + AStatsEvent_writeInt32(statsEvent, activity_start_msec); + 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; +} } // namespace -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { -// for (const auto& sampling_type : -// { GaugeMetric::FIRST_N_SAMPLES, GaugeMetric:: RANDOM_ONE_SAMPLE }) { -// auto config = CreateStatsdConfigForPushedEvent(sampling_type); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor( -// bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// int appUid1 = 123; -// int appUid2 = 456; -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(CreateMoveToBackgroundEvent(appUid1, bucketStartTimeNs + 15)); -// events.push_back(CreateMoveToForegroundEvent( -// appUid1, bucketStartTimeNs + bucketSizeNs + 250)); -// events.push_back(CreateMoveToBackgroundEvent( -// appUid1, bucketStartTimeNs + bucketSizeNs + 350)); -// events.push_back(CreateMoveToForegroundEvent( -// appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// -// -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::WARM, "activity_name1", "calling_pkg_name1", -// true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::HOT, "activity_name2", "calling_pkg_name2", -// true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::COLD, "activity_name3", "calling_pkg_name3", -// true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::WARM, "activity_name4", "calling_pkg_name4", -// true /*is_instant_app*/, 104 /*activity_start_msec*/, -// bucketStartTimeNs + bucketSizeNs + 30)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::COLD, "activity_name5", "calling_pkg_name5", -// true /*is_instant_app*/, 105 /*activity_start_msec*/, -// bucketStartTimeNs + 2 * bucketSizeNs)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid1, "app1", AppStartOccurred::HOT, "activity_name6", "calling_pkg_name6", -// false /*is_instant_app*/, 106 /*activity_start_msec*/, -// bucketStartTimeNs + 2 * bucketSizeNs + 10)); -// -// events.push_back(CreateMoveToBackgroundEvent( -// appUid2, bucketStartTimeNs + bucketSizeNs + 10)); -// events.push_back(CreateAppStartOccurredEvent( -// appUid2, "app2", AppStartOccurred::COLD, "activity_name7", "calling_pkg_name7", -// true /*is_instant_app*/, 201 /*activity_start_msec*/, -// bucketStartTimeNs + 2 * bucketSizeNs + 10)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics); -// EXPECT_EQ(2, gaugeMetrics.data_size()); -// -// auto data = gaugeMetrics.data(0); -// EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(3, data.bucket_info_size()); -// if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) { -// EXPECT_EQ(2, data.bucket_info(0).atom_size()); -// EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::HOT, -// data.bucket_info(0).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name2", -// data.bucket_info(0).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(102L, -// data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); -// EXPECT_EQ(AppStartOccurred::COLD, -// data.bucket_info(0).atom(1).app_start_occurred().type()); -// EXPECT_EQ("activity_name3", -// data.bucket_info(0).atom(1).app_start_occurred().activity_name()); -// EXPECT_EQ(103L, -// data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis()); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::WARM, -// data.bucket_info(1).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name4", -// data.bucket_info(1).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(104L, -// data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); -// -// EXPECT_EQ(2, data.bucket_info(2).atom_size()); -// EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::COLD, -// data.bucket_info(2).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name5", -// data.bucket_info(2).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(105L, -// data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); -// EXPECT_EQ(AppStartOccurred::HOT, -// data.bucket_info(2).atom(1).app_start_occurred().type()); -// EXPECT_EQ("activity_name6", -// data.bucket_info(2).atom(1).app_start_occurred().activity_name()); -// EXPECT_EQ(106L, -// data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis()); -// } else { -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::HOT, -// data.bucket_info(0).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name2", -// data.bucket_info(0).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(102L, -// data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); -// -// EXPECT_EQ(1, data.bucket_info(1).atom_size()); -// EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::WARM, -// data.bucket_info(1).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name4", -// data.bucket_info(1).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(104L, -// data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); -// -// EXPECT_EQ(1, data.bucket_info(2).atom_size()); -// EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::COLD, -// data.bucket_info(2).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name5", -// data.bucket_info(2).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(105L, -// data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); -// } -// -// data = gaugeMetrics.data(1); -// -// EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).atom_size()); -// EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type()); -// EXPECT_EQ("activity_name7", -// data.bucket_info(0).atom(0).app_start_occurred().activity_name()); -// EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); -// } -//} +TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { + for (const auto& sampling_type : + {GaugeMetric::FIRST_N_SAMPLES, GaugeMetric::RANDOM_ONE_SAMPLE}) { + auto config = CreateStatsdConfigForPushedEvent(sampling_type); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + int appUid1 = 123; + int appUid2 = 456; + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid1)); + events.push_back( + CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid1)); + events.push_back( + CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid1)); + events.push_back( + CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, appUid1)); + + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 10, appUid1, "app1", AppStartOccurred::WARM, "activity_name1", + "calling_pkg_name1", true /*is_instant_app*/, 101 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 20, appUid1, "app1", AppStartOccurred::HOT, "activity_name2", + "calling_pkg_name2", true /*is_instant_app*/, 102 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 30, appUid1, "app1", AppStartOccurred::COLD, "activity_name3", + "calling_pkg_name3", true /*is_instant_app*/, 103 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + bucketSizeNs + 30, appUid1, "app1", AppStartOccurred::WARM, + "activity_name4", "calling_pkg_name4", true /*is_instant_app*/, + 104 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 2 * bucketSizeNs, appUid1, "app1", AppStartOccurred::COLD, + "activity_name5", "calling_pkg_name5", true /*is_instant_app*/, + 105 /*activity_start_msec*/)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid1, "app1", AppStartOccurred::HOT, + "activity_name6", "calling_pkg_name6", false /*is_instant_app*/, + 106 /*activity_start_msec*/)); + + events.push_back( + CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 10, appUid2)); + events.push_back(CreateAppStartOccurredEvent( + bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid2, "app2", AppStartOccurred::COLD, + "activity_name7", "calling_pkg_name7", true /*is_instant_app*/, + 201 /*activity_start_msec*/)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::GaugeMetricDataWrapper gaugeMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), + &gaugeMetrics); + EXPECT_EQ(2, gaugeMetrics.data_size()); + + auto data = gaugeMetrics.data(0); + EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(3, data.bucket_info_size()); + if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) { + EXPECT_EQ(2, data.bucket_info(0).atom_size()); + EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::HOT, + data.bucket_info(0).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name2", + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(102L, + data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(0).atom(1).app_start_occurred().type()); + EXPECT_EQ("activity_name3", + data.bucket_info(0).atom(1).app_start_occurred().activity_name()); + EXPECT_EQ(103L, + data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis()); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::WARM, + data.bucket_info(1).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name4", + data.bucket_info(1).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(104L, + data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); + + EXPECT_EQ(2, data.bucket_info(2).atom_size()); + EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(2).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name5", + data.bucket_info(2).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(105L, + data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); + EXPECT_EQ(AppStartOccurred::HOT, + data.bucket_info(2).atom(1).app_start_occurred().type()); + EXPECT_EQ("activity_name6", + data.bucket_info(2).atom(1).app_start_occurred().activity_name()); + EXPECT_EQ(106L, + data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis()); + } else { + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::HOT, + data.bucket_info(0).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name2", + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(102L, + data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); + + EXPECT_EQ(1, data.bucket_info(1).atom_size()); + EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::WARM, + data.bucket_info(1).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name4", + data.bucket_info(1).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(104L, + data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); + + EXPECT_EQ(1, data.bucket_info(2).atom_size()); + EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(2).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name5", + data.bucket_info(2).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(105L, + data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); + } + + data = gaugeMetrics.data(1); + + EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).atom_size()); + EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type()); + EXPECT_EQ("activity_name7", + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); + } +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp index f3f7df775899..1dd90e2b9070 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -233,1609 +233,1602 @@ StatsdConfig CreateStatsdConfigWithTwoMetricsTwoDeactivations() { } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(MetricActivationE2eTest, TestCountMetric) { -// auto config = CreateStatsdConfig(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// 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, -// bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// std::unique_ptr<LogEvent> event; -// -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 0); -// -// // Activated by battery save mode. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// // First processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Activated by screen on event. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + 20); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// // 2nd processed event. -// // The activation by screen_on event expires, but the one by battery save mode is still active. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// // No new broadcast since the config should still be active. -// EXPECT_EQ(broadcastCount, 1); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// -// // All activations expired. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// // Re-activate metric via screen on. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// -// // 4th processed event. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(4, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { -// auto config = CreateStatsdConfigWithOneDeactivation(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// 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, -// bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap.size(), 1u); -// EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// std::unique_ptr<LogEvent> event; -// -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 0); -// -// // Activated by battery save mode. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // First processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Activated by screen on event. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + 20); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // 2nd processed event. -// // The activation by screen_on event expires, but the one by battery save mode is still active. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// // No new broadcast since the config should still be active. -// EXPECT_EQ(broadcastCount, 1); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// -// // All activations expired. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // Re-activate metric via screen on. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // 4th processed event. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // 5th processed event. -// event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// -// // Cancel battery saver mode activation. -// event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // Screen-on activation expired. -// event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 5); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// // Cancel battery saver mode activation. -// event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 6); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(5, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { -// auto config = CreateStatsdConfigWithTwoDeactivations(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// 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, -// bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap.size(), 2u); -// EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); -// EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[4].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// std::unique_ptr<LogEvent> event; -// -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 0); -// -// // Activated by battery save mode. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // First processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Activated by screen on event. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + 20); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // 2nd processed event. -// // The activation by screen_on event expires, but the one by battery save mode is still active. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// // No new broadcast since the config should still be active. -// EXPECT_EQ(broadcastCount, 1); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// -// // All activations expired. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // Re-activate metric via screen on. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // 4th processed event. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // 5th processed event. -// event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// -// // Cancel battery saver mode and screen on activation. -// event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // Screen-on activation expired. -// event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 5); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// // Cancel battery saver mode and screen on activation. -// event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 6); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(5, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { -// auto config = CreateStatsdConfigWithSameDeactivations(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// 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, -// bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap.size(), 1u); -// EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 2u); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]); -// EXPECT_EQ(broadcastCount, 0); -// -// std::unique_ptr<LogEvent> event; -// -// // Event that should be ignored. -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 1); -// -// // Activate metric via screen on for 2 minutes. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); -// -// // 1st processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Enable battery saver mode activation for 5 minutes. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); -// -// // 2nd processed event. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40); -// -// // Cancel battery saver mode and screen on activation. -// int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61; -// event = CreateScreenBrightnessChangedEvent(64, firstDeactivation); -// processor.OnLogEvent(event.get(), firstDeactivation); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// -// // Should be ignored -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 61 + 80); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); -// -// // Cancel battery saver mode activation. -// int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13; -// event = CreateScreenBrightnessChangedEvent(140, secondDeactivation); -// processor.OnLogEvent(event.get(), secondDeactivation); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// -// // Should be ignored. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(3, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); -//} -// -//TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { -// auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations(); -// -// int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; -// -// int uid = 12345; -// int64_t cfgId = 98765; -// ConfigKey cfgKey(uid, cfgId); -// -// 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, -// bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config); -// -// EXPECT_EQ(processor.mMetricsManagers.size(), 1u); -// sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; -// EXPECT_TRUE(metricsManager->isConfigValid()); -// EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 2); -// sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; -// auto& eventActivationMap = metricProducer->mEventActivationMap; -// auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; -// sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1]; -// auto& eventActivationMap2 = metricProducer2->mEventActivationMap; -// auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap; -// -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_FALSE(metricProducer2->mIsActive); -// // Two activations: one is triggered by battery saver mode (tracker index 0), the other is -// // triggered by screen on event (tracker index 2). -// EXPECT_EQ(eventActivationMap.size(), 2u); -// EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); -// EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap.size(), 2u); -// EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); -// EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[4].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// -// EXPECT_EQ(eventActivationMap2.size(), 2u); -// EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end()); -// EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end()); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, 0); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2.size(), 2u); -// EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end()); -// EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end()); -// EXPECT_EQ(eventDeactivationMap[3].size(), 1u); -// EXPECT_EQ(eventDeactivationMap[4].size(), 1u); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// std::unique_ptr<LogEvent> event; -// -// event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// event = CreateMoveToForegroundEvent(1111, bucketStartTimeNs + 5); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(broadcastCount, 0); -// -// // Activated by battery save mode. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // First processed event. -// event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// event = CreateMoveToForegroundEvent(2222, bucketStartTimeNs + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); -// -// // Activated by screen on event. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + 20); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // 2nd processed event. -// // The activation by screen_on event expires, but the one by battery save mode is still active. -// event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// event = CreateMoveToForegroundEvent(3333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// // No new broadcast since the config should still be active. -// EXPECT_EQ(broadcastCount, 1); -// -// // 3rd processed event. -// event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// event = CreateMoveToForegroundEvent(4444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); -// -// // All activations expired. -// event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// event = CreateMoveToForegroundEvent(5555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); -// EXPECT_FALSE(metricsManager->isActive()); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 2); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // Re-activate metric via screen on. -// event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // 4th processed event. -// event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// event = CreateMoveToForegroundEvent(6666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 3); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // 5th processed event. -// event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// event = CreateMoveToForegroundEvent(7777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); -// -// // Cancel battery saver mode and screen on activation. -// event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); -// EXPECT_FALSE(metricsManager->isActive()); -// // New broadcast since the config is no longer active. -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // Screen-on activation expired. -// event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// event = CreateMoveToForegroundEvent(8888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 4); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// event = CreateMoveToForegroundEvent(9999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); -// -// // Re-enable battery saver mode activation. -// event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_TRUE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 5); -// EXPECT_EQ(activeConfigsBroadcast.size(), 1); -// EXPECT_EQ(activeConfigsBroadcast[0], cfgId); -// EXPECT_TRUE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_TRUE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// // Cancel battery saver mode and screen on activation. -// event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 16); -// EXPECT_FALSE(metricsManager->isActive()); -// EXPECT_EQ(broadcastCount, 6); -// EXPECT_EQ(activeConfigsBroadcast.size(), 0); -// EXPECT_FALSE(metricProducer->mIsActive); -// EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); -// EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); -// EXPECT_FALSE(metricProducer2->mIsActive); -// EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); -// EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); -// EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); -// EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); -// EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); -// EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); -// EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(2, reports.reports(0).metrics_size()); -// EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); -// EXPECT_EQ(5, reports.reports(0).metrics(1).count_metrics().data_size()); -// -// StatsLogReport::CountMetricDataWrapper countMetrics; -// -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).count_metrics(), &countMetrics); -// EXPECT_EQ(5, countMetrics.data_size()); -// -// auto data = countMetrics.data(0); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// -// countMetrics.clear_data(); -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(1).count_metrics(), &countMetrics); -// EXPECT_EQ(5, countMetrics.data_size()); -// -// data = countMetrics.data(0); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(1); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(2); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// // Partial bucket as metric is deactivated. -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(3); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -// -// data = countMetrics.data(4); -// EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* uid field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); -// EXPECT_EQ(1, data.bucket_info_size()); -// EXPECT_EQ(1, data.bucket_info(0).count()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, -// data.bucket_info(0).end_bucket_elapsed_nanos()); -//} +TEST(MetricActivationE2eTest, TestCountMetric) { + auto config = CreateStatsdConfig(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + 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, bucketStartTimeNs, + [](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(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 0); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + // First processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + + // All activations expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + // Re-activate metric via screen on. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, + android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + + // 4th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(4, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { + auto config = CreateStatsdConfigWithOneDeactivation(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + 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, bucketStartTimeNs, + [](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(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap.size(), 1u); + EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 1u); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 0); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // First processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + + // All activations expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // Re-activate metric via screen on. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, + android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // 4th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // 5th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); + + // Cancel battery saver mode activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // Screen-on activation expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 5); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + // Cancel battery saver mode activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 6); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(5, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13, + data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { + auto config = CreateStatsdConfigWithTwoDeactivations(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + 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, bucketStartTimeNs, + [](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(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap.size(), 2u); + EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); + EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 1u); + EXPECT_EQ(eventDeactivationMap[4].size(), 1u); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 0); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // First processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + + // All activations expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // Re-activate metric via screen on. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, + android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // 4th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // 5th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); + + // Cancel battery saver mode and screen on activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // Screen-on activation expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 5); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + // Cancel battery saver mode and screen on activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 6); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(5, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { + auto config = CreateStatsdConfigWithSameDeactivations(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + 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, bucketStartTimeNs, + [](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(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap.size(), 1u); + EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 2u); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]); + EXPECT_EQ(broadcastCount, 0); + + std::unique_ptr<LogEvent> event; + + // Event that should be ignored. + event = CreateAppCrashEvent(bucketStartTimeNs + 1, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 1); + + // Activate metric via screen on for 2 minutes. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); + + // 1st processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Enable battery saver mode activation for 5 minutes. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10); + + // 2nd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 40, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40); + + // Cancel battery saver mode and screen on activation. + int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61; + event = CreateScreenBrightnessChangedEvent(firstDeactivation, 64); + processor.OnLogEvent(event.get(), firstDeactivation); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + + // Should be ignored + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 61 + 80, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); + + // Cancel battery saver mode activation. + int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13; + event = CreateScreenBrightnessChangedEvent(secondDeactivation, 140); + processor.OnLogEvent(event.get(), secondDeactivation); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + + // Should be ignored. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(3, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos()); +} + +TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { + auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations(); + + int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + 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, bucketStartTimeNs, + [](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(bucketStartTimeNs, cfgKey, config); + + EXPECT_EQ(processor.mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 2); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + auto& eventDeactivationMap = metricProducer->mEventDeactivationMap; + sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1]; + auto& eventActivationMap2 = metricProducer2->mEventActivationMap; + auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap; + + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_FALSE(metricProducer2->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap.size(), 2u); + EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); + EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 1u); + EXPECT_EQ(eventDeactivationMap[4].size(), 1u); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + + EXPECT_EQ(eventActivationMap2.size(), 2u); + EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end()); + EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end()); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, 0); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2.size(), 2u); + EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end()); + EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end()); + EXPECT_EQ(eventDeactivationMap[3].size(), 1u); + EXPECT_EQ(eventDeactivationMap[4].size(), 1u); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + 5, 1111); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(broadcastCount, 0); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // First processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + 15, 2222); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 3333); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + // No new broadcast since the config should still be active. + EXPECT_EQ(broadcastCount, 1); + + // 3rd processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 4444); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + + // All activations expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 5555); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); + EXPECT_FALSE(metricsManager->isActive()); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 2); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // Re-activate metric via screen on. + event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10, + android::view::DISPLAY_STATE_ON); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // 4th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 6666); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 3); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // 5th processed event. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 7777); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); + + // Cancel battery saver mode and screen on activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); + EXPECT_FALSE(metricsManager->isActive()); + // New broadcast since the config is no longer active. + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // Screen-on activation expired. + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 8888); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 4); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); + event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 9999); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); + + // Re-enable battery saver mode activation. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_TRUE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 5); + EXPECT_EQ(activeConfigsBroadcast.size(), 1); + EXPECT_EQ(activeConfigsBroadcast[0], cfgId); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_TRUE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + // Cancel battery saver mode and screen on activation. + event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); + EXPECT_FALSE(metricsManager->isActive()); + EXPECT_EQ(broadcastCount, 6); + EXPECT_EQ(activeConfigsBroadcast.size(), 0); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); + EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); + EXPECT_FALSE(metricProducer2->mIsActive); + EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); + EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]); + EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(2, reports.reports(0).metrics_size()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + EXPECT_EQ(5, reports.reports(0).metrics(1).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(5, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + countMetrics.clear_data(); + sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).count_metrics(), &countMetrics); + EXPECT_EQ(5, countMetrics.data_size()); + + data = countMetrics.data(0); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(4); + EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11, + data.bucket_info(0).end_bucket_elapsed_nanos()); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index 7d93fcced0ac..e8fb523deeb2 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -97,250 +97,247 @@ StatsdConfig CreateStatsdConfig() { } } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests, -//// we should use the real API which will clear the data after dump data is called. -//TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) { -// auto config = CreateStatsdConfig(); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// int appUid = 123; -// auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); -// auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); -// auto crashEvent3= CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); -// -// auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); -// auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); -// auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); -// -// auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); -// auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); -// -// auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); -// auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); -// -// auto screenTurnedOnEvent = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2); -// auto screenTurnedOffEvent = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 200); -// auto screenTurnedOnEvent2 = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2 * bucketSizeNs - 100); -// -// std::vector<AttributionNodeInternal> attributions = { -// CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; -// auto syncOnEvent1 = -// CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); -// auto syncOffEvent1 = -// CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); -// auto syncOnEvent2 = -// CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); -// -// auto moveToBackgroundEvent1 = -// CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); -// auto moveToForegroundEvent1 = -// CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); -// -// auto moveToBackgroundEvent2 = -// CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); -// auto moveToForegroundEvent2 = -// CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); -// -// /* -// bucket #1 bucket #2 -// -// -// | | | | | | | | | | (crashEvents) -// |-------------------------------------|-----------------------------------|--------- -// -// | | (MoveToBkground) -// -// | | (MoveToForeground) -// -// | | (SyncIsOn) -// | (SyncIsOff) -// | | (ScreenIsOn) -// | (ScreenIsOff) -// */ -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(std::move(crashEvent1)); -// events.push_back(std::move(crashEvent2)); -// events.push_back(std::move(crashEvent3)); -// events.push_back(std::move(crashEvent4)); -// events.push_back(std::move(crashEvent5)); -// events.push_back(std::move(crashEvent6)); -// events.push_back(std::move(crashEvent7)); -// events.push_back(std::move(crashEvent8)); -// events.push_back(std::move(crashEvent9)); -// events.push_back(std::move(crashEvent10)); -// events.push_back(std::move(screenTurnedOnEvent)); -// events.push_back(std::move(screenTurnedOffEvent)); -// events.push_back(std::move(screenTurnedOnEvent2)); -// events.push_back(std::move(syncOnEvent1)); -// events.push_back(std::move(syncOffEvent1)); -// events.push_back(std::move(syncOnEvent2)); -// events.push_back(std::move(moveToBackgroundEvent1)); -// events.push_back(std::move(moveToForegroundEvent1)); -// events.push_back(std::move(moveToBackgroundEvent2)); -// events.push_back(std::move(moveToForegroundEvent2)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// // Validate dimension value. -// EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); -// // Uid field. -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); -//} -// -//TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) { -// auto config = CreateStatsdConfig(); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor( -// bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// -// int appUid = 123; -// auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); -// auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); -// auto crashEvent3 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); -// -// auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); -// auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); -// auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); -// -// auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); -// auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); -// -// auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); -// auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); -// -// auto screenTurnedOnEvent = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2); -// auto screenTurnedOffEvent = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); -// auto screenTurnedOnEvent2 = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + 2 * bucketSizeNs - 100); -// -// std::vector<AttributionNodeInternal> attributions = { -// CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; -// auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); -// auto syncOffEvent1 = -// CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); -// auto syncOnEvent2 = -// CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); -// -// auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); -// auto moveToForegroundEvent1 = -// CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); -// -// auto moveToBackgroundEvent2 = -// CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); -// auto moveToForegroundEvent2 = -// CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); -// -// /* -// bucket #1 bucket #2 -// -// -// | | | | | | | | | | (crashEvents) -// |-------------------------------------|-----------------------------------|--------- -// -// | | (MoveToBkground) -// -// | | (MoveToForeground) -// -// | | (SyncIsOn) -// | (SyncIsOff) -// | | (ScreenIsOn) -// | (ScreenIsOff) -// */ -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back(std::move(crashEvent1)); -// events.push_back(std::move(crashEvent2)); -// events.push_back(std::move(crashEvent3)); -// events.push_back(std::move(crashEvent4)); -// events.push_back(std::move(crashEvent5)); -// events.push_back(std::move(crashEvent6)); -// events.push_back(std::move(crashEvent7)); -// events.push_back(std::move(crashEvent8)); -// events.push_back(std::move(crashEvent9)); -// events.push_back(std::move(crashEvent10)); -// events.push_back(std::move(screenTurnedOnEvent)); -// events.push_back(std::move(screenTurnedOffEvent)); -// events.push_back(std::move(screenTurnedOnEvent2)); -// events.push_back(std::move(syncOnEvent1)); -// events.push_back(std::move(syncOffEvent1)); -// events.push_back(std::move(syncOnEvent2)); -// events.push_back(std::move(moveToBackgroundEvent1)); -// events.push_back(std::move(moveToForegroundEvent1)); -// events.push_back(std::move(moveToBackgroundEvent2)); -// events.push_back(std::move(moveToForegroundEvent2)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); -// auto data = reports.reports(0).metrics(0).count_metrics().data(0); -// // Validate dimension value. -// EXPECT_EQ(data.dimensions_in_what().field(), -// android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); -// // Uid field. -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); -// EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); -//} +// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests, +// we should use the real API which will clear the data after dump data is called. +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) { + auto config = CreateStatsdConfig(); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + int appUid = 123; + auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid); + auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid); + auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid); + + auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid); + auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid); + auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid); + + auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid); + auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid); + + auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid); + auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid); + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + + std::vector<int> attributionUids = {appUid, appUid + 1}; + std::vector<string> attributionTags = {"App1", "GMSCoreModule1"}; + + auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids, + attributionTags, "ReadEmail"); + auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids, + attributionTags, "ReadEmail"); + auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000, + attributionUids, attributionTags, "ReadDoc"); + + auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid); + auto moveToForegroundEvent1 = + CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid); + + auto moveToBackgroundEvent2 = + CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid); + auto moveToForegroundEvent2 = + CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid); + + /* + bucket #1 bucket #2 + + + | | | | | | | | | | (crashEvents) + |-------------------------------------|-----------------------------------|--------- + + | | (MoveToBkground) + + | | (MoveToForeground) + + | | (SyncIsOn) + | (SyncIsOff) + | | (ScreenIsOn) + | (ScreenIsOff) + */ + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(std::move(crashEvent1)); + events.push_back(std::move(crashEvent2)); + events.push_back(std::move(crashEvent3)); + events.push_back(std::move(crashEvent4)); + events.push_back(std::move(crashEvent5)); + events.push_back(std::move(crashEvent6)); + events.push_back(std::move(crashEvent7)); + events.push_back(std::move(crashEvent8)); + events.push_back(std::move(crashEvent9)); + events.push_back(std::move(crashEvent10)); + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(syncOnEvent1)); + events.push_back(std::move(syncOffEvent1)); + events.push_back(std::move(syncOnEvent2)); + events.push_back(std::move(moveToBackgroundEvent1)); + events.push_back(std::move(moveToForegroundEvent1)); + events.push_back(std::move(moveToBackgroundEvent2)); + events.push_back(std::move(moveToForegroundEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + // Validate dimension value. + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + // Uid field. + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); +} + +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) { + auto config = CreateStatsdConfig(); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + int appUid = 123; + auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid); + auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid); + auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid); + + auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid); + auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid); + auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid); + + auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid); + auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid); + + auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid); + auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid); + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + + std::vector<int> attributionUids = {appUid, appUid + 1}; + std::vector<string> attributionTags = {"App1", "GMSCoreModule1"}; + + auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids, + attributionTags, "ReadEmail"); + auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids, + attributionTags, "ReadEmail"); + auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000, + attributionUids, attributionTags, "ReadDoc"); + + auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid); + auto moveToForegroundEvent1 = + CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid); + + auto moveToBackgroundEvent2 = + CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid); + auto moveToForegroundEvent2 = + CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid); + + /* + bucket #1 bucket #2 + + + | | | | | | | | | | (crashEvents) + |-------------------------------------|-----------------------------------|--------- + + | | (MoveToBkground) + + | | (MoveToForeground) + + | | (SyncIsOn) + | (SyncIsOff) + | | (ScreenIsOn) + | (ScreenIsOff) + */ + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(std::move(crashEvent1)); + events.push_back(std::move(crashEvent2)); + events.push_back(std::move(crashEvent3)); + events.push_back(std::move(crashEvent4)); + events.push_back(std::move(crashEvent5)); + events.push_back(std::move(crashEvent6)); + events.push_back(std::move(crashEvent7)); + events.push_back(std::move(crashEvent8)); + events.push_back(std::move(crashEvent9)); + events.push_back(std::move(crashEvent10)); + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(syncOnEvent1)); + events.push_back(std::move(syncOffEvent1)); + events.push_back(std::move(syncOnEvent2)); + events.push_back(std::move(moveToBackgroundEvent1)); + events.push_back(std::move(moveToForegroundEvent1)); + events.push_back(std::move(moveToBackgroundEvent2)); + events.push_back(std::move(moveToForegroundEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + // Validate dimension value. + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + // Uid field. + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 9ec831b567bf..b97590761785 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -113,96 +113,107 @@ StatsdConfig MakeGaugeMetricConfig(int64_t minTime) { } } // anonymous namespace -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// SendConfig(service, MakeConfig()); -// int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are -// // initialized with. -// -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get()); -// -// ConfigMetricsReport report = GetReports(service->mProcessor, start + 3); -// // Expect no metrics since the bucket has not finished yet. -// EXPECT_EQ(1, report.metrics_size()); -// EXPECT_EQ(0, report.metrics(0).count_metrics().data_size()); -//} -// -//TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// SendConfig(service, MakeConfig()); -// int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are -// // initialized with. -// -// // Force the uidmap to update at timestamp 2. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); -// // This is a new installation, so there shouldn't be a split (should be same as the without -// // split case). -// service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), -// String16("")); -// // Goes into the second bucket. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); -// -// ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); -// EXPECT_EQ(1, report.metrics_size()); -// EXPECT_EQ(0, report.metrics(0).count_metrics().data_size()); -//} -// -//TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// SendConfig(service, MakeConfig()); -// int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are -// // initialized with. -// service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, -// {String16("")}); -// -// // Force the uidmap to update at timestamp 2. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); -// service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), -// String16("")); -// // Goes into the second bucket. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); -// -// ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); -// backfillStartEndTimestamp(&report); -// -// ASSERT_EQ(1, report.metrics_size()); -// ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); -// ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); -// EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0). -// has_start_bucket_elapsed_nanos()); -// EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0). -// has_end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); -//} -// -//TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// SendConfig(service, MakeConfig()); -// int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are -// // initialized with. -// service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, -// {String16("")}); -// -// // Force the uidmap to update at timestamp 2. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); -// service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1); -// // Goes into the second bucket. -// service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); -// -// ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); -// backfillStartEndTimestamp(&report); -// -// ASSERT_EQ(1, report.metrics_size()); -// ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); -// ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); -// EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0). -// has_start_bucket_elapsed_nanos()); -// EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0). -// has_end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); -//} +TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 2, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 3); + // Expect no metrics since the bucket has not finished yet. + EXPECT_EQ(1, report.metrics_size()); + EXPECT_EQ(0, report.metrics(0).count_metrics().data_size()); +} + +TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + + // Force the uidmap to update at timestamp 2. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); + // This is a new installation, so there shouldn't be a split (should be same as the without + // split case). + service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), + String16("")); + // Goes into the second bucket. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + EXPECT_EQ(1, report.metrics_size()); + EXPECT_EQ(0, report.metrics(0).count_metrics().data_size()); +} + +TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, + {String16("")}); + + // Force the uidmap to update at timestamp 2. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); + service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), + String16("")); + // Goes into the second bucket. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + backfillStartEndTimestamp(&report); + + ASSERT_EQ(1, report.metrics_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_start_bucket_elapsed_nanos()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_end_bucket_elapsed_nanos()); + EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); +} + +TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())}, + {String16("")}); + + // Force the uidmap to update at timestamp 2. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get()); + service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1); + // Goes into the second bucket. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 4); + backfillStartEndTimestamp(&report); + + ASSERT_EQ(1, report.metrics_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_start_bucket_elapsed_nanos()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_end_bucket_elapsed_nanos()); + EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); +} TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp index 99dbaf17c85c..a87bb71b12bc 100644 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp @@ -64,317 +64,313 @@ StatsdConfig CreateStatsdConfig(bool useCondition = true) { } // namespace -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(ValueMetricE2eTest, TestPulledEvents) { -// auto config = CreateStatsdConfig(); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// android::util::SUBSYSTEM_SLEEP_STATE); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// // When creating the config, the value metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& expectedPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); -// -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 65); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 75); -// processor->OnLogEvent(screenOffEvent.get()); -// -// // Pulling alarm arrives on time and reset the sequential pulling alarm. -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 2 * bucketSizeNs + 15); -// processor->OnLogEvent(screenOnEvent.get()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 4 * bucketSizeNs + 11); -// processor->OnLogEvent(screenOffEvent.get()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::ValueMetricDataWrapper valueMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).value_metrics(), &valueMetrics); -// EXPECT_GT((int)valueMetrics.data_size(), 1); -// -// auto data = valueMetrics.data(0); -// EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// // We have 4 buckets, the first one was incomplete since the condition was unknown. -// EXPECT_EQ(4, data.bucket_info_size()); -// -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(0).values_size()); -// -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(1).values_size()); -// -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(2).values_size()); -// -// EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(3).values_size()); -//} -// -//TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { -// auto config = CreateStatsdConfig(); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// // 10 mins == 2 bucket durations. -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// android::util::SUBSYSTEM_SLEEP_STATE); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// -// // When creating the config, the value metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& expectedPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); -// -// // Screen off/on/off events. -// auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 55); -// processor->OnLogEvent(screenOffEvent.get()); -// -// auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 65); -// processor->OnLogEvent(screenOnEvent.get()); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 75); -// processor->OnLogEvent(screenOffEvent.get()); -// -// // Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the -// // future, data will be skipped. -// processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); -// -// // This screen state change will start a new bucket. -// screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, -// configAddedTimeNs + 4 * bucketSizeNs + 65); -// processor->OnLogEvent(screenOnEvent.get()); -// -// // The alarm is delayed but we already created a bucket thanks to the screen state condition. -// // This bucket does not have to be skipped since the alarm arrives in time for the next bucket. -// processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); -// -// screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, -// configAddedTimeNs + 6 * bucketSizeNs + 31); -// processor->OnLogEvent(screenOffEvent.get()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::ValueMetricDataWrapper valueMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).value_metrics(), &valueMetrics); -// EXPECT_GT((int)valueMetrics.data_size(), 1); -// -// auto data = valueMetrics.data(0); -// EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// EXPECT_EQ(3, data.bucket_info_size()); -// -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(0).values_size()); -// -// EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(1).values_size()); -// -// EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, data.bucket_info(2).values_size()); -//} -// -//TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { -// auto config = CreateStatsdConfig(false); -// int64_t baseTimeNs = getElapsedRealtimeNs(); -// int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; -// int64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; -// -// auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); -// *config.add_atom_matcher() = batterySaverStartMatcher; -// const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. -// auto metric_activation = config.add_metric_activation(); -// metric_activation->set_metric_id(metricId); -// metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); -// auto event_activation = metric_activation->add_event_activation(); -// event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); -// event_activation->set_ttl_seconds(ttlNs / 1000000000); -// -// ConfigKey cfgKey; -// auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, -// SharedRefBase::make<FakeSubsystemSleepCallback>(), -// android::util::SUBSYSTEM_SLEEP_STATE); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// processor->mPullerManager->ForceClearPullerCache(); -// -// int startBucketNum = processor->mMetricsManagers.begin()->second-> -// mAllMetricProducers[0]->getCurrentBucketNum(); -// EXPECT_GT(startBucketNum, (int64_t)0); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // When creating the config, the value metric producer should register the alarm at the -// // end of the current bucket. -// EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); -// EXPECT_EQ(bucketSizeNs, -// processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); -// int64_t& expectedPullTimeNs = -// processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); -// -// // Pulling alarm arrives on time and reset the sequential pulling alarm. -// processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// // Activate the metric. A pull occurs here -// const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. -// auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); -// processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns. -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); -// -// // Create random event to deactivate metric. -// auto deactivationEvent = CreateScreenBrightnessChangedEvent(50, activationNs + ttlNs + 1); -// processor->OnLogEvent(deactivationEvent.get()); -// EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 3); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs); -// -// processor->informPullAlarmFired(expectedPullTimeNs + 4); -// EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); -// -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(1, reports.reports_size()); -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// StatsLogReport::ValueMetricDataWrapper valueMetrics; -// sortMetricDataByDimensionsValue( -// reports.reports(0).metrics(0).value_metrics(), &valueMetrics); -// EXPECT_GT((int)valueMetrics.data_size(), 0); -// -// auto data = valueMetrics.data(0); -// EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); -// EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); -// EXPECT_EQ(1 /* subsystem name field */, -// data.dimensions_in_what().value_tuple().dimensions_value(0).field()); -// EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); -// // We have 2 full buckets, the two surrounding the activation are dropped. -// EXPECT_EQ(2, data.bucket_info_size()); -// -// auto bucketInfo = data.bucket_info(0); -// EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, bucketInfo.values_size()); -// -// bucketInfo = data.bucket_info(1); -// EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); -// EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); -// EXPECT_EQ(1, bucketInfo.values_size()); -//} +TEST(ValueMetricE2eTest, TestPulledEvents) { + auto config = CreateStatsdConfig(); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), + android::util::SUBSYSTEM_SLEEP_STATE); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + // When creating the config, the value metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& expectedPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); + + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + auto screenOnEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + // Pulling alarm arrives on time and reset the sequential pulling alarm. + processor->informPullAlarmFired(expectedPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 2 * bucketSizeNs + 15, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 11, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::ValueMetricDataWrapper valueMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); + EXPECT_GT((int)valueMetrics.data_size(), 1); + + auto data = valueMetrics.data(0); + EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + // We have 4 buckets, the first one was incomplete since the condition was unknown. + EXPECT_EQ(4, data.bucket_info_size()); + + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(0).values_size()); + + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(1).values_size()); + + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(2).values_size()); + + EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(3).values_size()); +} + +TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { + auto config = CreateStatsdConfig(); + int64_t baseTimeNs = getElapsedRealtimeNs(); + // 10 mins == 2 bucket durations. + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), + android::util::SUBSYSTEM_SLEEP_STATE); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + + // When creating the config, the value metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& expectedPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); + + // Screen off/on/off events. + auto screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + auto screenOnEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + screenOffEvent = + CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + // Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the + // future, data will be skipped. + processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); + + // This screen state change will start a new bucket. + screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 65, + android::view::DISPLAY_STATE_ON); + processor->OnLogEvent(screenOnEvent.get()); + + // The alarm is delayed but we already created a bucket thanks to the screen state condition. + // This bucket does not have to be skipped since the alarm arrives in time for the next bucket. + processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); + + screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 6 * bucketSizeNs + 31, + android::view::DISPLAY_STATE_OFF); + processor->OnLogEvent(screenOffEvent.get()); + + processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::ValueMetricDataWrapper valueMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); + EXPECT_GT((int)valueMetrics.data_size(), 1); + + auto data = valueMetrics.data(0); + EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + EXPECT_EQ(3, data.bucket_info_size()); + + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(0).values_size()); + + EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(1).values_size()); + + EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, data.bucket_info(2).values_size()); +} + +TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { + auto config = CreateStatsdConfig(false); + int64_t baseTimeNs = getElapsedRealtimeNs(); + int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000; + + auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher(); + *config.add_atom_matcher() = batterySaverStartMatcher; + const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. + auto metric_activation = config.add_metric_activation(); + metric_activation->set_metric_id(metricId); + metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto event_activation = metric_activation->add_event_activation(); + event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); + event_activation->set_ttl_seconds(ttlNs / 1000000000); + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey, + SharedRefBase::make<FakeSubsystemSleepCallback>(), + android::util::SUBSYSTEM_SLEEP_STATE); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mPullerManager->ForceClearPullerCache(); + + int startBucketNum = processor->mMetricsManagers.begin() + ->second->mAllMetricProducers[0] + ->getCurrentBucketNum(); + EXPECT_GT(startBucketNum, (int64_t)0); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // When creating the config, the value metric producer should register the alarm at the + // end of the current bucket. + EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size()); + EXPECT_EQ(bucketSizeNs, + processor->mPullerManager->mReceivers.begin()->second.front().intervalNs); + int64_t& expectedPullTimeNs = + processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs; + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs); + + // Pulling alarm arrives on time and reset the sequential pulling alarm. + processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + // Activate the metric. A pull occurs here + const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis. + auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs); + processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms. + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs); + + processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns. + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs); + + // Create random event to deactivate metric. + auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50); + processor->OnLogEvent(deactivationEvent.get()); + EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive()); + + processor->informPullAlarmFired(expectedPullTimeNs + 3); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs); + + processor->informPullAlarmFired(expectedPullTimeNs + 4); + EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true, + ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + StatsLogReport::ValueMetricDataWrapper valueMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics); + EXPECT_GT((int)valueMetrics.data_size(), 0); + + auto data = valueMetrics.data(0); + EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* subsystem name field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty()); + // We have 2 full buckets, the two surrounding the activation are dropped. + EXPECT_EQ(2, data.bucket_info_size()); + + auto bucketInfo = data.bucket_info(0); + EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(1, bucketInfo.values_size()); + + bucketInfo = data.bucket_info(1); + EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos()); + EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos()); + EXPECT_EQ(1, bucketInfo.values_size()); +} /** * Test initialization of a simple value metric that is sliced by a state. diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp index 21092e2260d9..ddd8f956737e 100644 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -61,292 +61,290 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) return config; } -std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"), - CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; - -std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"), - CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; - -// TODO(b/149590301): Update this helper to use new socket schema. -///* -//Events: -//Screen off is met from (200ns,1 min+500ns]. -//Acquire event for wl1 from 2ns to 1min+2ns -//Acquire event for wl2 from 1min-10ns to 2min-15ns -//*/ -//void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) { -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// -// auto screenTurnedOnEvent = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1); -// auto screenTurnedOffEvent = CreateScreenStateChangedEvent( -// android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200); -// auto screenTurnedOnEvent2 = -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// bucketStartTimeNs + bucketSizeNs + 500); -// -// auto acquireEvent1 = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2); -// auto releaseEvent1 = -// CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2); -// auto acquireEvent2 = -// CreateAcquireWakelockEvent(attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10); -// auto releaseEvent2 = CreateReleaseWakelockEvent(attributions2, "wl2", -// bucketStartTimeNs + 2 * bucketSizeNs - 15); -// -// std::vector<std::unique_ptr<LogEvent>> events; -// -// events.push_back(std::move(screenTurnedOnEvent)); -// events.push_back(std::move(screenTurnedOffEvent)); -// events.push_back(std::move(screenTurnedOnEvent2)); -// events.push_back(std::move(acquireEvent1)); -// events.push_back(std::move(acquireEvent2)); -// events.push_back(std::move(releaseEvent1)); -// events.push_back(std::move(releaseEvent2)); -// -// sortLogEventsByTimestamp(&events); -// -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -//} +std::vector<int> attributionUids1 = {111, 222, 222}; +std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; + +std::vector<int> attributionUids2 = {111, 222, 222}; +std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; + +/* +Events: +Screen off is met from (200ns,1 min+500ns]. +Acquire event for wl1 from 2ns to 1min+2ns +Acquire event for wl2 from 1min-10ns to 2min-15ns +*/ +void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) { + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + + auto screenTurnedOnEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + auto screenTurnedOffEvent = CreateScreenStateChangedEvent( + bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 500, + android::view::DisplayStateEnum::DISPLAY_STATE_ON); + + auto acquireEvent1 = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + auto releaseEvent1 = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, + attributionUids1, attributionTags1, "wl1"); + auto acquireEvent2 = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 10, + attributionUids2, attributionTags2, "wl2"); + auto releaseEvent2 = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 15, + attributionUids2, attributionTags2, "wl2"); + + std::vector<std::unique_ptr<LogEvent>> events; + + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(acquireEvent1)); + events.push_back(std::move(acquireEvent2)); + events.push_back(std::move(releaseEvent1)); + events.push_back(std::move(releaseEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } +} } // namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::SUM); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// // Only 1 dimension output. The tag dimension in the predicate has been aggregated. -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // Validate bucket info. -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); -// data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // The wakelock holding interval starts from the screen off event and to the end of the 1st -// // bucket. -// EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); -//} -// -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::SUM); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// // Dump the report after the end of 2nd bucket. -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // Two output buckets. -// // The wakelock holding interval in the 1st bucket starts from the screen off event and to -// // the end of the 1st bucket. -// EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), -// bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200)); -// // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and -// // ends at the second screen on event. -// EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); -//} -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::SUM); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// vector<uint8_t> buffer; -// ConfigMetricsReportList reports; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 2 * bucketSizeNs + 90)); -// events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3", -// bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3", -// bucketStartTimeNs + 5 * bucketSizeNs + 100)); -// sortLogEventsByTimestamp(&events); -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // The last wakelock holding spans 4 buckets. -// EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100); -// EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs); -// EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs); -// EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL); -//} -// -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// -// EXPECT_EQ(reports.reports_size(), 1); -// -// // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as -// // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by -// // itself. -// EXPECT_EQ(1, reports.reports(0).metrics_size()); -// EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size()); -//} -// -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// // Dump the report after the end of 2nd bucket. One dimension with one bucket. -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// // Validate dimension value. -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // The max is acquire event for wl1 to screen off start. -// EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200); -//} -// -//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) { -// ConfigKey cfgKey; -// auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); -// uint64_t bucketStartTimeNs = 10000000000; -// uint64_t bucketSizeNs = -// TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; -// auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); -// EXPECT_EQ(processor->mMetricsManagers.size(), 1u); -// EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); -// FeedEvents(config, processor); -// ConfigMetricsReportList reports; -// vector<uint8_t> buffer; -// -// std::vector<std::unique_ptr<LogEvent>> events; -// events.push_back( -// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, -// bucketStartTimeNs + 2 * bucketSizeNs + 90)); -// events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3", -// bucketStartTimeNs + 2 * bucketSizeNs + 100)); -// events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3", -// bucketStartTimeNs + 5 * bucketSizeNs + 100)); -// sortLogEventsByTimestamp(&events); -// for (const auto& event : events) { -// processor->OnLogEvent(event.get()); -// } -// -// processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, -// ADB_DUMP, FAST, &buffer); -// EXPECT_TRUE(buffer.size() > 0); -// EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); -// backfillDimensionPath(&reports); -// backfillStringInReport(&reports); -// backfillStartEndTimestamp(&reports); -// EXPECT_EQ(reports.reports_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); -// EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); -// auto data = reports.reports(0).metrics(0).duration_metrics().data(0); -// ValidateAttributionUidDimension(data.dimensions_in_what(), -// android::util::WAKELOCK_STATE_CHANGED, 111); -// // The last wakelock holding spans 4 buckets. -// EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs); -// EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(), -// bucketStartTimeNs + 5 * bucketSizeNs); -// EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(), -// bucketStartTimeNs + 6 * bucketSizeNs); -//} +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + // Only 1 dimension output. The tag dimension in the predicate has been aggregated. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // Validate bucket info. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); + data = reports.reports(0).metrics(0).duration_metrics().data(0); + // The wakelock holding interval starts from the screen off event and to the end of the 1st + // bucket. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + // Dump the report after the end of 2nd bucket. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // Two output buckets. + // The wakelock holding interval in the 1st bucket starts from the screen off event and to + // the end of the 1st bucket. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), + bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200)); + // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and + // ends at the second screen on event. + EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::SUM); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + vector<uint8_t> buffer; + ConfigMetricsReportList reports; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back( + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, + attributionUids1, attributionTags1, "wl3")); + events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100, + attributionUids1, attributionTags1, "wl3")); + sortLogEventsByTimestamp(&events); + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The last wakelock holding spans 4 buckets. + EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100); + EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + + EXPECT_EQ(reports.reports_size(), 1); + + // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as + // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by + // itself. + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size()); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + // Dump the report after the end of 2nd bucket. One dimension with one bucket. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The max is acquire event for wl1 to screen off start. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200); +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) { + ConfigKey cfgKey; + auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + FeedEvents(config, processor); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back( + CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90, + android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); + events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, + attributionUids1, attributionTags1, "wl3")); + events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100, + attributionUids1, attributionTags1, "wl3")); + sortLogEventsByTimestamp(&events); + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP, + FAST, &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); + // The last wakelock holding spans 4 buckets. + EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(), + bucketStartTimeNs + 5 * bucketSizeNs); + EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(), + bucketStartTimeNs + 6 * bucketSizeNs); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp index c0b4f436530f..e8200d5c7f52 100644 --- a/cmds/statsd/tests/external/StatsPuller_test.cpp +++ b/cmds/statsd/tests/external/StatsPuller_test.cpp @@ -15,11 +15,14 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdio.h> + #include <chrono> #include <thread> #include <vector> + #include "../metrics/metrics_test_helper.h" #include "src/stats_log_util.h" +#include "stats_event.h" #include "tests/statsd_test_util.h" #ifdef __ANDROID__ @@ -57,13 +60,22 @@ private: FakePuller puller; -// TODO(b/149590301): Update this helper to use new socket schema. -//shared_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) { -// shared_ptr<LogEvent> event = make_shared<LogEvent>(pullTagId, eventTimeNs); -// event->write(value); -// event->init(); -// return event; -//} +std::unique_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, pullTagId); + AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); + + AStatsEvent_writeInt64(statsEvent, value); + 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; +} class StatsPullerTest : public ::testing::Test { public: @@ -80,149 +92,148 @@ public: } // Anonymous namespace. -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST_F(StatsPullerTest, PullSuccess) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = true; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -// -// sleep_for(std::chrono::seconds(1)); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = true; -// -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value); -//} -// -//TEST_F(StatsPullerTest, PullFailAfterSuccess) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = true; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -// -// sleep_for(std::chrono::seconds(1)); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = false; -// dataHolder.clear(); -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -// -// pullSuccess = true; -// dataHolder.clear(); -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} -// -//// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown -//TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// pullSuccess = true; -// // timeout is 0.5 -// pullDelayNs = (long)(0.8 * NS_PER_SEC); -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = true; -// dataHolder.clear(); -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} -// -//TEST_F(StatsPullerTest, PullFail) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = false; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} -// -//TEST_F(StatsPullerTest, PullTakeTooLong) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = true; -// pullDelayNs = NS_PER_SEC; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} -// -//TEST_F(StatsPullerTest, PullTooFast) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = true; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = true; -// -// dataHolder.clear(); -// EXPECT_TRUE(puller.Pull(&dataHolder)); -// EXPECT_EQ(1, dataHolder.size()); -// EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); -// EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); -// EXPECT_EQ(1, dataHolder[0]->size()); -// EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); -//} -// -//TEST_F(StatsPullerTest, PullFailsAndTooFast) { -// pullData.push_back(createSimpleEvent(1111L, 33)); -// -// pullSuccess = false; -// -// vector<std::shared_ptr<LogEvent>> dataHolder; -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -// -// pullData.clear(); -// pullData.push_back(createSimpleEvent(2222L, 44)); -// -// pullSuccess = true; -// -// EXPECT_FALSE(puller.Pull(&dataHolder)); -// EXPECT_EQ(0, dataHolder.size()); -//} +TEST_F(StatsPullerTest, PullSuccess) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = true; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); + + sleep_for(std::chrono::seconds(1)); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = true; + + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value); +} + +TEST_F(StatsPullerTest, PullFailAfterSuccess) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = true; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); + + sleep_for(std::chrono::seconds(1)); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = false; + dataHolder.clear(); + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); + + pullSuccess = true; + dataHolder.clear(); + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown +TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) { + pullData.push_back(createSimpleEvent(1111L, 33)); + pullSuccess = true; + // timeout is 0.5 + pullDelayNs = (long)(0.8 * NS_PER_SEC); + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = true; + dataHolder.clear(); + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +TEST_F(StatsPullerTest, PullFail) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = false; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +TEST_F(StatsPullerTest, PullTakeTooLong) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = true; + pullDelayNs = NS_PER_SEC; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +TEST_F(StatsPullerTest, PullTooFast) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = true; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = true; + + dataHolder.clear(); + EXPECT_TRUE(puller.Pull(&dataHolder)); + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value); +} + +TEST_F(StatsPullerTest, PullFailsAndTooFast) { + pullData.push_back(createSimpleEvent(1111L, 33)); + + pullSuccess = false; + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); + + pullData.clear(); + pullData.push_back(createSimpleEvent(2222L, 44)); + + pullSuccess = true; + + EXPECT_FALSE(puller.Pull(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp index 81590a2d1e43..f21954f20d08 100644 --- a/cmds/statsd/tests/external/puller_util_test.cpp +++ b/cmds/statsd/tests/external/puller_util_test.cpp @@ -13,12 +13,16 @@ // limitations under the License. #include "external/puller_util.h" + #include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdio.h> + #include <vector> -#include "statslog.h" + #include "../metrics/metrics_test_helper.h" +#include "stats_event.h" +#include "statslog.h" #ifdef __ANDROID__ @@ -58,212 +62,187 @@ void extractIntoVector(vector<shared_ptr<LogEvent>> events, ret.push_back(vec); } } + +std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int data1, int data2) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, uidAtomTagId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeInt32(statsEvent, data1); + AStatsEvent_writeInt32(statsEvent, data2); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::shared_ptr<LogEvent> makeNonUidAtomLogEvent(uint64_t timestampNs, int data1) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, nonUidAtomTagId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, data1); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + } // anonymous namespace -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(puller_util, MergeNoDimension) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 30->22->31 -// event->write(isolatedUid); -// event->write(hostNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->22->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(hostNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) -// .WillRepeatedly(Return(hostUid)); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) -// .WillRepeatedly(ReturnArg<0>()); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 22, 52}; -// EXPECT_EQ(1, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -//} -// -//TEST(puller_util, MergeWithDimension) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 30->32->31 -// event->write(isolatedUid); -// event->write(isolatedNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->32->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(isolatedNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->22->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(hostNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) -// .WillRepeatedly(Return(hostUid)); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) -// .WillRepeatedly(ReturnArg<0>()); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 22, 21}; -// vector<int> expectedV2 = {20, 32, 52}; -// EXPECT_EQ(2, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -// EXPECT_THAT(actual, Contains(expectedV2)); -//} -// -//TEST(puller_util, NoMergeHostUidOnly) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 20->32->31 -// event->write(hostUid); -// event->write(isolatedNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->22->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(hostNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) -// .WillRepeatedly(Return(hostUid)); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) -// .WillRepeatedly(ReturnArg<0>()); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// // 20->32->31 -// // 20->22->21 -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 32, 31}; -// vector<int> expectedV2 = {20, 22, 21}; -// EXPECT_EQ(2, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -// EXPECT_THAT(actual, Contains(expectedV2)); -//} -// -//TEST(puller_util, IsolatedUidOnly) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 30->32->31 -// event->write(hostUid); -// event->write(isolatedNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 30->22->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(hostNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)) -// .WillRepeatedly(Return(hostUid)); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))) -// .WillRepeatedly(ReturnArg<0>()); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// // 20->32->31 -// // 20->22->21 -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 32, 31}; -// vector<int> expectedV2 = {20, 22, 21}; -// EXPECT_EQ(2, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -// EXPECT_THAT(actual, Contains(expectedV2)); -//} -// -//TEST(puller_util, MultipleIsolatedUidToOneHostUid) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// // 30->32->31 -// event->write(isolatedUid); -// event->write(isolatedNonAdditiveData); -// event->write(isolatedAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 31->32->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(isolatedUid + 1); -// event->write(isolatedNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// // 20->32->21 -// event = make_shared<LogEvent>(uidAtomTagId, timestamp); -// event->write(hostUid); -// event->write(isolatedNonAdditiveData); -// event->write(hostAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid)); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); -// -// vector<vector<int>> actual; -// extractIntoVector(inputData, actual); -// vector<int> expectedV1 = {20, 32, 73}; -// EXPECT_EQ(1, (int)actual.size()); -// EXPECT_THAT(actual, Contains(expectedV1)); -//} -// -//TEST(puller_util, NoNeedToMerge) { -// vector<shared_ptr<LogEvent>> inputData; -// shared_ptr<LogEvent> event = -// make_shared<LogEvent>(nonUidAtomTagId, timestamp); -// // 32 -// event->write(isolatedNonAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// event = make_shared<LogEvent>(nonUidAtomTagId, timestamp); -// // 22 -// event->write(hostNonAdditiveData); -// event->init(); -// inputData.push_back(event); -// -// sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); -// mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId, {} /*no additive fields*/); -// -// EXPECT_EQ(2, (int)inputData.size()); -//} +TEST(puller_util, MergeNoDimension) { + vector<shared_ptr<LogEvent>> inputData; + + // 30->22->31 + inputData.push_back( + makeUidLogEvent(timestamp, isolatedUid, hostNonAdditiveData, isolatedAdditiveData)); + + // 20->22->21 + inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 22, 52}; + EXPECT_EQ(1, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); +} + +TEST(puller_util, MergeWithDimension) { + vector<shared_ptr<LogEvent>> inputData; + + // 30->32->31 + inputData.push_back( + makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData)); + + // 20->32->21 + inputData.push_back( + makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData)); + + // 20->22->21 + inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 22, 21}; + vector<int> expectedV2 = {20, 32, 52}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, NoMergeHostUidOnly) { + vector<shared_ptr<LogEvent>> inputData; + + // 20->32->31 + inputData.push_back( + makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData)); + + // 20->22->21 + inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + // 20->32->31 + // 20->22->21 + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 32, 31}; + vector<int> expectedV2 = {20, 22, 21}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, IsolatedUidOnly) { + vector<shared_ptr<LogEvent>> inputData; + + // 30->32->31 + inputData.push_back( + makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData)); + + // 30->22->21 + inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + // 20->32->31 + // 20->22->21 + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 32, 31}; + vector<int> expectedV2 = {20, 22, 21}; + EXPECT_EQ(2, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); + EXPECT_THAT(actual, Contains(expectedV2)); +} + +TEST(puller_util, MultipleIsolatedUidToOneHostUid) { + vector<shared_ptr<LogEvent>> inputData; + + // 30->32->31 + inputData.push_back( + makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData)); + + // 31->32->21 + inputData.push_back( + makeUidLogEvent(timestamp, isolatedUid + 1, isolatedNonAdditiveData, hostAdditiveData)); + + // 20->32->21 + inputData.push_back( + makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid)); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + + vector<vector<int>> actual; + extractIntoVector(inputData, actual); + vector<int> expectedV1 = {20, 32, 73}; + EXPECT_EQ(1, (int)actual.size()); + EXPECT_THAT(actual, Contains(expectedV1)); +} + +TEST(puller_util, NoNeedToMerge) { + vector<shared_ptr<LogEvent>> inputData; + + // 32 + inputData.push_back(makeNonUidAtomLogEvent(timestamp, isolatedNonAdditiveData)); + + // 22 + inputData.push_back(makeNonUidAtomLogEvent(timestamp, hostNonAdditiveData)); + + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId, {} /*no additive fields*/); + + EXPECT_EQ(2, (int)inputData.size()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp index c4407f48d978..6dc041f9fb6e 100644 --- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp +++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp @@ -42,7 +42,7 @@ std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) { size_t size; uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/-1, /*pid=*/-1); + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); logEvent->parseBuffer(buf, size); AStatsEvent_release(statsEvent); return logEvent; diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index b8826780c7b9..d55996cb1b7a 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -13,16 +13,19 @@ // limitations under the License. #include "src/metrics/CountMetricProducer.h" -#include "src/stats_log_util.h" -#include "metrics_test_helper.h" -#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> #include <math.h> #include <stdio.h> + #include <vector> +#include "metrics_test_helper.h" +#include "src/stats_log_util.h" +#include "stats_event.h" +#include "tests/statsd_test_util.h" + using namespace testing; using android::sp; using std::set; @@ -37,366 +40,392 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(CountMetricProducerTest, TestFirstBucket) { -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5, -// 600 * NS_PER_SEC + NS_PER_SEC / 2); -// EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(10, countProducer.mCurrentBucketNum); -// EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); -//} -// -//TEST(CountMetricProducerTest, TestNonDimensionalEvents) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; -// int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; -// int tagId = 1; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + 2); -// event2.init(); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// // 2 events in bucket 1. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// -// // Flushes at event #2. -// countProducer.flushIfNeededLocked(bucketStartTimeNs + 2); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// // Flushes. -// countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(2LL, buckets[0].mCount); -// -// // 1 matched event happens in bucket 2. -// LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2); -// event3.init(); -// -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); -// countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1]; -// EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs); -// EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs); -// EXPECT_EQ(1LL, bucketInfo2.mCount); -// -// // nothing happens in bucket 3. we should not record anything for bucket 3. -// countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(2UL, buckets3.size()); -//} -// -//TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_condition(StringToId("SCREEN_ON")); -// -// LogEvent event1(1, bucketStartTimeNs + 1); -// event1.init(); -// -// LogEvent event2(1, bucketStartTimeNs + 10); -// event2.init(); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, -// bucketStartTimeNs); -// -// countProducer.onConditionChanged(true, bucketStartTimeNs); -// countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2); -// // Upon this match event, the matched event1 is flushed. -// countProducer.onMatchedLogEvent(1 /*matcher index*/, event2); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// { -// const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// const auto& bucketInfo = buckets[0]; -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); -// EXPECT_EQ(1LL, bucketInfo.mCount); -// } -//} -// -//TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// int tagId = 1; -// int conditionTagId = 2; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// 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); -// event1.write("111"); // uid -// event1.init(); -// ConditionKey key1; -// key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = -// {getMockedDimensionKey(conditionTagId, 2, "111")}; -// -// LogEvent event2(tagId, bucketStartTimeNs + 10); -// event2.write("222"); // uid -// 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)); -// -// CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); -// EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// countProducer.mPastBuckets.end()); -// const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// const auto& bucketInfo = buckets[0]; -// EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); -// EXPECT_EQ(1LL, bucketInfo.mCount); -//} -// -//TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { -// sp<AlarmMonitor> alarmMonitor; -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -// -// int tagId = 1; -// int conditionTagId = 2; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// Alert alert; -// alert.set_num_buckets(3); -// alert.set_trigger_if_sum_gt(2); -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.write("111"); // uid -// event1.init(); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); -// EXPECT_TRUE(anomalyTracker != nullptr); -// -// // Bucket is flushed yet. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // App upgrade forces bucket flush. -// // Check that there's a past bucket and the bucket end is not adjusted. -// countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((long long)bucketStartTimeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); -// EXPECT_EQ((long long)eventUpgradeTimeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); -// EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); -// // Anomaly tracker only contains full buckets. -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs(); -// // Next event occurs in same bucket as partial bucket created. -// LogEvent event2(tagId, bucketStartTimeNs + 59 * NS_PER_SEC + 10); -// event2.write("222"); // uid -// event2.init(); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // Third event in following bucket. -// LogEvent event3(tagId, bucketStartTimeNs + 62 * NS_PER_SEC + 10); -// event3.write("333"); // uid -// event3.init(); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); -// EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -//} -// -//TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; -// -// int tagId = 1; -// int conditionTagId = 2; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.write("111"); // uid -// event1.init(); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// // Bucket is flushed yet. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); -// -// // App upgrade forces bucket flush. -// // Check that there's a past bucket and the bucket end is not adjusted. -// countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((int64_t)bucketStartTimeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); -// EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); -// -// // Next event occurs in same bucket as partial bucket created. -// LogEvent event2(tagId, bucketStartTimeNs + 70 * NS_PER_SEC + 10); -// event2.write("222"); // uid -// event2.init(); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// -// // Third event in following bucket. -// LogEvent event3(tagId, bucketStartTimeNs + 121 * NS_PER_SEC + 10); -// event3.write("333"); // uid -// event3.init(); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); -// EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((int64_t)eventUpgradeTimeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, -// countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); -//} -// -//TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { -// sp<AlarmMonitor> alarmMonitor; -// Alert alert; -// alert.set_id(11); -// alert.set_metric_id(1); -// alert.set_trigger_if_sum_gt(2); -// alert.set_num_buckets(2); -// const int32_t refPeriodSec = 1; -// alert.set_refractory_period_secs(refPeriodSec); -// -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; -// int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; -// -// CountMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// bucketStartTimeNs, bucketStartTimeNs); -// -// sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); -// -// int tagId = 1; -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + 2); -// event2.init(); -// LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1); -// event3.init(); -// LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1); -// event4.init(); -// LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2); -// event5.init(); -// LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3); -// event6.init(); -// LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC); -// event7.init(); -// -// // Two events in bucket #0. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); -// -// EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); -// EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); -// -// // One event in bucket #2. No alarm as bucket #0 is trashed out. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); -// EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); -// EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); -// -// // Two events in bucket #3. -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5); -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6); -// EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); -// EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); -// // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), -// std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -// -// countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); -// EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); -// EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), -// std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -//} +namespace { + +void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} + +void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeString(statsEvent, uid.c_str()); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} + +} // namespace + +TEST(CountMetricProducerTest, TestFirstBucket) { + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5, + 600 * NS_PER_SEC + NS_PER_SEC / 2); + EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(10, countProducer.mCurrentBucketNum); + EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); +} + +TEST(CountMetricProducerTest, TestNonDimensionalEvents) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; + int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; + int tagId = 1; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + // 2 events in bucket 1. + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + + // Flushes at event #2. + countProducer.flushIfNeededLocked(bucketStartTimeNs + 2); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + // Flushes. + countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(2LL, buckets[0].mCount); + + // 1 matched event happens in bucket 2. + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + + countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1]; + EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs); + EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs); + EXPECT_EQ(1LL, bucketInfo2.mCount); + + // nothing happens in bucket 3. we should not record anything for bucket 3. + countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(2UL, buckets3.size()); +} + +TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_condition(StringToId("SCREEN_ON")); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, + bucketStartTimeNs); + + countProducer.onConditionChanged(true, bucketStartTimeNs); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1); + countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); + + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2); + + // Upon this match event, the matched event1 is flushed. + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1); + countProducer.onMatchedLogEvent(1 /*matcher index*/, event2); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + const auto& bucketInfo = buckets[0]; + EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); + EXPECT_EQ(1LL, bucketInfo.mCount); +} + +TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + int tagId = 1; + int conditionTagId = 2; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + 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, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); + + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222"); + + ConditionKey key1; + key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = { + getMockedDimensionKey(conditionTagId, 2, "111")}; + + 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)); + + CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + countProducer.mPastBuckets.end()); + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + const auto& bucketInfo = buckets[0]; + EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs); + EXPECT_EQ(1LL, bucketInfo.mCount); +} + +TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { + sp<AlarmMonitor> alarmMonitor; + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + + int tagId = 1; + int conditionTagId = 2; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + Alert alert; + alert.set_num_buckets(3); + alert.set_trigger_if_sum_gt(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); + EXPECT_TRUE(anomalyTracker != nullptr); + + // Bucket is flushed yet. + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // App upgrade forces bucket flush. + // Check that there's a past bucket and the bucket end is not adjusted. + countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((long long)bucketStartTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ((long long)eventUpgradeTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + // Anomaly tracker only contains full buckets. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs(); + // Next event occurs in same bucket as partial bucket created. + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Third event in following bucket. + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + + int tagId = 1; + int conditionTagId = 2; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + // Bucket is flushed yet. + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); + + // App upgrade forces bucket flush. + // Check that there's a past bucket and the bucket end is not adjusted. + countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)bucketStartTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + + // Next event occurs in same bucket as partial bucket created. + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + + // Third event in following bucket. + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)eventUpgradeTimeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); +} + +TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { + sp<AlarmMonitor> alarmMonitor; + Alert alert; + alert.set_id(11); + alert.set_metric_id(1); + alert.set_trigger_if_sum_gt(2); + alert.set_num_buckets(2); + const int32_t refPeriodSec = 1; + alert.set_refractory_period_secs(refPeriodSec); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; + int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; + + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + bucketStartTimeNs, bucketStartTimeNs); + + sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId); + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId); + LogEvent event5(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId); + LogEvent event6(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId); + LogEvent event7(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId); + + // Two events in bucket #0. + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + + EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); + EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); + + // One event in bucket #2. No alarm as bucket #0 is trashed out. + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); + EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); + + // Two events in bucket #3. + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6); + EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); + EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); + // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), + std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); + + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); + EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); + EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), + std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); +} TEST(CountMetricProducerTest, TestOneWeekTimeUnit) { CountMetric metric; diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 66613749aaea..6143dc0dc5d1 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -13,17 +13,20 @@ // limitations under the License. #include "src/metrics/DurationMetricProducer.h" -#include "src/stats_log_util.h" -#include "metrics_test_helper.h" -#include "src/condition/ConditionWizard.h" #include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdio.h> + #include <set> #include <unordered_map> #include <vector> +#include "metrics_test_helper.h" +#include "src/condition/ConditionWizard.h" +#include "src/stats_log_util.h" +#include "stats_event.h" + using namespace android::os::statsd; using namespace testing; using android::sp; @@ -39,6 +42,22 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); +namespace { + +void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} + +} // namespace + TEST(DurationMetricTrackerTest, TestFirstBucket) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); DurationMetric metric; @@ -56,383 +75,386 @@ TEST(DurationMetricTrackerTest, TestFirstBucket) { EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs()); } -// TODO(b/149590301): Update these to use new socket schema. -//TEST(DurationMetricTrackerTest, TestNoCondition) { -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// int tagId = 1; -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2); -// event2.init(); -// -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event1); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); -// EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// durationProducer.mPastBuckets.end()); -// const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(2UL, buckets.size()); -// EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs); -// EXPECT_EQ(2LL, buckets[1].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// int tagId = 1; -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + 2); -// event2.init(); -// LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); -// event3.init(); -// LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); -// event4.init(); -// -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// durationProducer.mCondition = ConditionState::kFalse; -// -// EXPECT_FALSE(durationProducer.mCondition); -// EXPECT_FALSE(durationProducer.isConditionSliced()); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event1); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event3); -// durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); -// EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != -// durationProducer.mPastBuckets.end()); -// const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets2.size()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); -// EXPECT_EQ(1LL, buckets2[0].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// -// int tagId = 1; -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// event1.init(); -// LogEvent event2(tagId, bucketStartTimeNs + 2); -// event2.init(); -// LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); -// event3.init(); -// LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); -// event4.init(); -// -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); -// EXPECT_FALSE(durationProducer.isConditionSliced()); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event1); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// -// durationProducer.onMatchedLogEvent(1 /* start index*/, event3); -// durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); -// const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets2.size()); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); -// EXPECT_EQ(1LL, buckets2[0].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { -// /** -// * The duration starts from the first bucket, through the two partial buckets (10-70sec), -// * another bucket, and ends at the beginning of the next full bucket. -// * Expected buckets: -// * - [10,25]: 14 secs -// * - [25,70]: All 45 secs -// * - [70,130]: All 60 secs -// * - [130, 210]: Only 5 secs (event ended at 135sec) -// */ -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; -// int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; -// -// int tagId = 1; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// std::vector<DurationBucket> buckets = -// durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(3UL, buckets.size()); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); -// EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { -// /** -// * Expected buckets (start at 11s, upgrade at 75s, end at 135s): -// * - [10,70]: 59 secs -// * - [70,75]: 5 sec -// * - [75,130]: 55 secs -// */ -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; -// int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; -// -// int tagId = 1; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// std::vector<DurationBucket> buckets = -// durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); -// EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(3UL, buckets.size()); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { -// sp<AlarmMonitor> alarmMonitor; -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1; -// int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; -// -// int tagId = 1; -// -// // Setup metric with alert. -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_SUM); -// Alert alert; -// alert.set_num_buckets(3); -// alert.set_trigger_if_sum_gt(2); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); -// EXPECT_TRUE(anomalyTracker != nullptr); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// -// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, -// anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -//} -// -//TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1; -// int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; -// -// int tagId = 1; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); -// LogEvent event1(tagId, startTimeNs); -// event1.write("111"); // uid -// event1.init(); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); -// std::vector<DurationBucket> buckets = -// durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); -//} -// -//TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; -// int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; -// int64_t startTimeNs = bucketStartTimeNs + 1; -// int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; -// -// int tagId = 1; -// -// DurationMetric metric; -// metric.set_id(1); -// metric.set_bucket(ONE_MINUTE); -// metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); -// LogEvent event1(tagId, startTimeNs); -// event1.write("111"); // uid -// event1.init(); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// FieldMatcher dimensions; -// DurationMetricProducer durationProducer( -// kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, -// 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); -// -// LogEvent start_event(tagId, startTimeNs); -// start_event.init(); -// durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); -// EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// // Stop occurs in the same partial bucket as created for the app upgrade. -// LogEvent end_event(tagId, endTimeNs); -// end_event.init(); -// durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); -// EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); -// -// durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); -// std::vector<DurationBucket> buckets = -// durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; -// EXPECT_EQ(1UL, buckets.size()); -// EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); -// EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); -// EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); -//} +TEST(DurationMetricTrackerTest, TestNoCondition) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + durationProducer.mPastBuckets.end()); + const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(2UL, buckets.size()); + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(2LL, buckets[1].mDuration); +} + +TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId); + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, 0 /* condition index */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + durationProducer.mCondition = ConditionState::kFalse; + + EXPECT_FALSE(durationProducer.mCondition); + EXPECT_FALSE(durationProducer.isConditionSliced()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event3); + durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != + durationProducer.mPastBuckets.end()); + const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets2.size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); + EXPECT_EQ(1LL, buckets2[0].mDuration); +} + +TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, bucketStartTimeNs + 1, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, bucketStartTimeNs + 2, tagId); + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId); + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId); + + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, 0 /* condition index */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); + EXPECT_FALSE(durationProducer.isConditionSliced()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event3); + durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); + durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); + const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets2.size()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); + EXPECT_EQ(1LL, buckets2[0].mDuration); +} + +TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { + /** + * The duration starts from the first bucket, through the two partial buckets (10-70sec), + * another bucket, and ends at the beginning of the next full bucket. + * Expected buckets: + * - [10,25]: 14 secs + * - [25,70]: All 45 secs + * - [70,130]: All 60 secs + * - [130, 210]: Only 5 secs (event ended at 135sec) + */ + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + std::vector<DurationBucket> buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(3UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); + EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); +} + +TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { + /** + * Expected buckets (start at 11s, upgrade at 75s, end at 135s): + * - [10,70]: 59 secs + * - [70,75]: 5 sec + * - [75,130]: 55 secs + */ + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + std::vector<DurationBucket> buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(3UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); +} + +TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { + sp<AlarmMonitor> alarmMonitor; + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1; + int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + // Setup metric with alert. + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_SUM); + Alert alert; + alert.set_num_buckets(3); + alert.set_trigger_if_sum_gt(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); + EXPECT_TRUE(anomalyTracker != nullptr); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, + anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1; + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); + std::vector<DurationBucket> buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); +} + +TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; + int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + int64_t startTimeNs = bucketStartTimeNs + 1; + int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; + + int tagId = 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); + + DurationMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_MINUTE); + metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + FieldMatcher dimensions; + DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, + 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, + dimensions, bucketStartTimeNs, bucketStartTimeNs); + + durationProducer.onMatchedLogEvent(1 /* start index*/, event1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); + EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + // Stop occurs in the same partial bucket as created for the app upgrade. + durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + + durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); + std::vector<DurationBucket> buckets = + durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; + EXPECT_EQ(1UL, buckets.size()); + EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index d416f1395727..e2eee032a43b 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -410,40 +410,75 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) return dimensions; } -// TODO(b/149590301): Update these helpers to use new socket schema. -//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); -// EXPECT_TRUE(event->write(state)); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>( -// android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs); -// EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON)); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>( -// android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs); -// EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF)); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent( -// int level, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs); -// EXPECT_TRUE(event->write(level)); -// event->init(); -// return event; -// -//} -// +std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( + uint64_t timestampNs, const android::view::DisplayStateEnum state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::SCREEN_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + 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> CreateBatterySaverOnEvent(uint64_t timestampNs) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::BATTERY_SAVER_MODE_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON); + 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> CreateBatterySaverOffEvent(uint64_t timestampNs) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::BATTERY_SAVER_MODE_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF); + 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> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::SCREEN_BRIGHTNESS_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, level); + 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) { @@ -470,121 +505,212 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) // attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs); //} // -//std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, -// const WakelockStateChanged::State state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs); -// event->write(attributions); -// event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); -// event->write(wakelockName); -// event->write(state); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateAcquireWakelockEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, -// uint64_t timestampNs) { -// return CreateWakelockStateChangedEvent( -// attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, -// uint64_t timestampNs) { -// return CreateWakelockStateChangedEvent( -// attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( -// const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>( -// android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs); -// event->write(uid); -// event->write("pkg_name"); -// event->write("class_name"); -// event->write(state); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) { -// return CreateActivityForegroundStateChangedEvent( -// uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) { -// return CreateActivityForegroundStateChangedEvent( -// uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateSyncStateChangedEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& name, -// const SyncStateChanged::State state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs); -// event->write(attributions); -// event->write(name); -// event->write(state); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateSyncStartEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& name, -// uint64_t timestampNs) { -// return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateSyncEndEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& name, -// uint64_t timestampNs) { -// return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent( -// const int uid, const ProcessLifeCycleStateChanged::State state, uint64_t timestampNs) { -// auto logEvent = std::make_unique<LogEvent>( -// android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs); -// logEvent->write(uid); -// logEvent->write(""); -// logEvent->write(state); -// logEvent->init(); -// return logEvent; -//} -// -//std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) { -// return CreateProcessLifeCycleStateChangedEvent( -// uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs); -//} -// -//std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(const int uid, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::APP_CRASH_OCCURRED, timestampNs); -// event->write(uid); -// event->write("eventType"); -// event->write("processName"); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent( -// int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs) { -// auto logEvent = std::make_unique<LogEvent>( -// android::util::ISOLATED_UID_CHANGED, timestampNs); -// logEvent->write(hostUid); -// logEvent->write(isolatedUid); -// logEvent->write(is_create); -// logEvent->init(); -// return logEvent; -//} -// -//std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( -// int uid, const android::app::ProcessStateEnum state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, timestampNs); -// event->write(uid); -// event->write(state); -// event->init(); -// return event; -//} +std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& wakelockName, + const WakelockStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::WAKELOCK_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, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); + AStatsEvent_writeString(statsEvent, wakelockName.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(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& wakelockName) { + return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags, + wakelockName, WakelockStateChanged::ACQUIRE); +} + +std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& wakelockName) { + return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags, + wakelockName, WakelockStateChanged::RELEASE); +} + +std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( + uint64_t timestampNs, const int uid, const ActivityForegroundStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, "pkg_name"); + AStatsEvent_writeString(statsEvent, "class_name"); + 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> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid) { + return CreateActivityForegroundStateChangedEvent(timestampNs, uid, + ActivityForegroundStateChanged::BACKGROUND); +} + +std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid) { + return CreateActivityForegroundStateChangedEvent(timestampNs, uid, + ActivityForegroundStateChanged::FOREGROUND); +} + +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, android::util::SYNC_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeString(statsEvent, name.c_str()); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name) { + return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, + SyncStateChanged::ON); +} + +std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name) { + return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, + SyncStateChanged::OFF); +} + +std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent( + uint64_t timestampNs, const int uid, const ProcessLifeCycleStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, ""); + 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> CreateAppCrashEvent(uint64_t timestampNs, const int uid) { + return CreateProcessLifeCycleStateChangedEvent(timestampNs, uid, + ProcessLifeCycleStateChanged::CRASHED); +} + +std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::APP_CRASH_OCCURRED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, "eventType"); + AStatsEvent_writeString(statsEvent, "processName"); + 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> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid, + int isolatedUid, bool is_create) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::ISOLATED_UID_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, hostUid); + AStatsEvent_writeInt32(statsEvent, isolatedUid); + AStatsEvent_writeInt32(statsEvent, is_create); + 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> CreateUidProcessStateChangedEvent( + uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, android::util::UID_PROCESS_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + 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, diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index c8326eef5698..437101578397 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -166,11 +166,10 @@ FieldMatcher CreateAttributionUidDimensions(const int atomId, // Create log event for screen state changed. std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - const android::view::DisplayStateEnum state, uint64_t timestampNs); + uint64_t timestampNs, const android::view::DisplayStateEnum state); // Create log event for screen brightness state changed. -std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent( - int level, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level); // Create log event when scheduled job starts. std::unique_ptr<LogEvent> CreateStartScheduledJobEvent( @@ -188,45 +187,42 @@ std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs); std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs); // Create log event for app moving to background. -std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid); // Create log event for app moving to foreground. -std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid); // 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>& uids, + const vector<string>& tags, 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); +std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, const vector<int>& uids, + const vector<string>& tags, const string& name); // Create log event when the app sync ends. -std::unique_ptr<LogEvent> CreateAppCrashEvent( - const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid); // Create log event for an app crash. -std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid); // Create log event for acquiring wakelock. -std::unique_ptr<LogEvent> CreateAcquireWakelockEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs, const vector<int>& uids, + const vector<string>& tags, + const string& wakelockName); // Create log event for releasing wakelock. -std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs, const vector<int>& uids, + const vector<string>& tags, + const string& wakelockName); // 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> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid, + int isolatedUid, bool is_create); // Create log event for uid process state change. std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( - int uid, const android::app::ProcessStateEnum state, uint64_t timestampNs); + uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state); // Helper function to create an AttributionNodeInternal proto. AttributionNodeInternal CreateAttribution(const int& uid, const string& tag); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index f613df2ac595..6564dc9b0840 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -162,7 +162,7 @@ import java.util.function.Supplier; * might also make sense inside of a single app if the access is forwarded between two features of * the app. * - * <p>An app can register an {@link AppOpsCollector} to get informed about what accesses the + * <p>An app can register an {@link OnOpNotedCallback} to get informed about what accesses the * system is tracking for it. As each runtime permission has an associated app-op this API is * particularly useful for an app that want to find unexpected private data accesses. */ @@ -206,16 +206,16 @@ public class AppOpsManager { private static final Object sLock = new Object(); - /** Current {@link AppOpsCollector}. Change via {@link #setNotedAppOpsCollector} */ + /** Current {@link OnOpNotedCallback}. Change via {@link #setOnOpNotedCallback} */ @GuardedBy("sLock") - private static @Nullable AppOpsCollector sNotedAppOpsCollector; + private static @Nullable OnOpNotedCallback sOnOpNotedCallback; /** * Additional collector that collect accesses and forwards a few of them them via * {@link IAppOpsService#reportRuntimeAppOpAccessMessageAndGetConfig}. */ - private static AppOpsCollector sMessageCollector = - new AppOpsCollector() { + private static OnOpNotedCallback sMessageCollector = + new OnOpNotedCallback() { @Override public void onNoted(@NonNull SyncNotedAppOp op) { reportStackTraceIfNeeded(op); @@ -1060,9 +1060,12 @@ public class AppOpsManager { /** @hide Access telephony call audio */ public static final int OP_ACCESS_CALL_AUDIO = 96; + /** @hide Auto-revoke app permissions if app is unused for an extended period */ + public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97; + /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 97; + public static final int _NUM_OP = 98; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1357,6 +1360,11 @@ public class AppOpsManager { @SystemApi public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio"; + /** @hide Auto-revoke app permissions if app is unused for an extended period */ + @SystemApi + public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = + "android:auto_revoke_permissions_if_unused"; + /** @hide Communicate cross-profile within the same profile group. */ @SystemApi public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles"; @@ -1446,6 +1454,7 @@ public class AppOpsManager { OP_INTERACT_ACROSS_PROFILES, OP_LOADER_USAGE_STATS, OP_ACCESS_CALL_AUDIO, + OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, }; /** @@ -1554,6 +1563,7 @@ public class AppOpsManager { OP_ACTIVATE_PLATFORM_VPN, // ACTIVATE_PLATFORM_VPN OP_LOADER_USAGE_STATS, // LOADER_USAGE_STATS OP_ACCESS_CALL_AUDIO, // ACCESS_CALL_AUDIO + OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED }; /** @@ -1657,6 +1667,7 @@ public class AppOpsManager { OPSTR_ACTIVATE_PLATFORM_VPN, OPSTR_LOADER_USAGE_STATS, OPSTR_ACCESS_CALL_AUDIO, + OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, }; /** @@ -1761,6 +1772,7 @@ public class AppOpsManager { "ACTIVATE_PLATFORM_VPN", "LOADER_USAGE_STATS", "ACCESS_CALL_AUDIO", + "AUTO_REVOKE_PERMISSIONS_IF_UNUSED", }; /** @@ -1866,6 +1878,7 @@ public class AppOpsManager { null, // no permission for OP_ACTIVATE_PLATFORM_VPN android.Manifest.permission.LOADER_USAGE_STATS, Manifest.permission.ACCESS_CALL_AUDIO, + null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED }; /** @@ -1971,110 +1984,112 @@ public class AppOpsManager { null, // ACTIVATE_PLATFORM_VPN null, // LOADER_USAGE_STATS null, // ACCESS_CALL_AUDIO + null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED }; /** - * This specifies whether each option should allow the system - * (and system ui) to bypass the user restriction when active. - */ - private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] { - true, //COARSE_LOCATION - true, //FINE_LOCATION - false, //GPS - false, //VIBRATE - false, //READ_CONTACTS - false, //WRITE_CONTACTS - false, //READ_CALL_LOG - false, //WRITE_CALL_LOG - false, //READ_CALENDAR - false, //WRITE_CALENDAR - true, //WIFI_SCAN - false, //POST_NOTIFICATION - false, //NEIGHBORING_CELLS - false, //CALL_PHONE - false, //READ_SMS - false, //WRITE_SMS - false, //RECEIVE_SMS - false, //RECEIVE_EMERGECY_SMS - false, //RECEIVE_MMS - false, //RECEIVE_WAP_PUSH - false, //SEND_SMS - false, //READ_ICC_SMS - false, //WRITE_ICC_SMS - false, //WRITE_SETTINGS - true, //SYSTEM_ALERT_WINDOW - false, //ACCESS_NOTIFICATIONS - false, //CAMERA - false, //RECORD_AUDIO - false, //PLAY_AUDIO - false, //READ_CLIPBOARD - false, //WRITE_CLIPBOARD - false, //TAKE_MEDIA_BUTTONS - false, //TAKE_AUDIO_FOCUS - false, //AUDIO_MASTER_VOLUME - false, //AUDIO_VOICE_VOLUME - false, //AUDIO_RING_VOLUME - false, //AUDIO_MEDIA_VOLUME - false, //AUDIO_ALARM_VOLUME - false, //AUDIO_NOTIFICATION_VOLUME - false, //AUDIO_BLUETOOTH_VOLUME - false, //WAKE_LOCK - false, //MONITOR_LOCATION - false, //MONITOR_HIGH_POWER_LOCATION - false, //GET_USAGE_STATS - false, //MUTE_MICROPHONE - true, //TOAST_WINDOW - false, //PROJECT_MEDIA - false, //ACTIVATE_VPN - false, //WALLPAPER - false, //ASSIST_STRUCTURE - false, //ASSIST_SCREENSHOT - false, //READ_PHONE_STATE - false, //ADD_VOICEMAIL - false, // USE_SIP - false, // PROCESS_OUTGOING_CALLS - false, // USE_FINGERPRINT - false, // BODY_SENSORS - false, // READ_CELL_BROADCASTS - false, // MOCK_LOCATION - false, // READ_EXTERNAL_STORAGE - false, // WRITE_EXTERNAL_STORAGE - false, // TURN_ON_SCREEN - false, // GET_ACCOUNTS - false, // RUN_IN_BACKGROUND - false, // AUDIO_ACCESSIBILITY_VOLUME - false, // READ_PHONE_NUMBERS - false, // REQUEST_INSTALL_PACKAGES - false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE - false, // INSTANT_APP_START_FOREGROUND - false, // ANSWER_PHONE_CALLS - false, // OP_RUN_ANY_IN_BACKGROUND - false, // OP_CHANGE_WIFI_STATE - false, // OP_REQUEST_DELETE_PACKAGES - false, // OP_BIND_ACCESSIBILITY_SERVICE - false, // ACCEPT_HANDOVER - false, // MANAGE_IPSEC_HANDOVERS - false, // START_FOREGROUND - true, // BLUETOOTH_SCAN - false, // USE_BIOMETRIC - false, // ACTIVITY_RECOGNITION - false, // SMS_FINANCIAL_TRANSACTIONS - false, // READ_MEDIA_AUDIO - false, // WRITE_MEDIA_AUDIO - false, // READ_MEDIA_VIDEO - false, // WRITE_MEDIA_VIDEO - false, // READ_MEDIA_IMAGES - false, // WRITE_MEDIA_IMAGES - false, // LEGACY_STORAGE - false, // ACCESS_ACCESSIBILITY - false, // READ_DEVICE_IDENTIFIERS - false, // ACCESS_MEDIA_LOCATION - false, // QUERY_ALL_PACKAGES - false, // MANAGE_EXTERNAL_STORAGE - false, // INTERACT_ACROSS_PROFILES - false, // ACTIVATE_PLATFORM_VPN - false, // LOADER_USAGE_STATS - false, // ACCESS_CALL_AUDIO + * In which cases should an app be allowed to bypass the {@link #setUserRestriction user + * restriction} for a certain app-op. + */ + private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] { + new RestrictionBypass(true, false), //COARSE_LOCATION + new RestrictionBypass(true, false), //FINE_LOCATION + null, //GPS + null, //VIBRATE + null, //READ_CONTACTS + null, //WRITE_CONTACTS + null, //READ_CALL_LOG + null, //WRITE_CALL_LOG + null, //READ_CALENDAR + null, //WRITE_CALENDAR + new RestrictionBypass(true, false), //WIFI_SCAN + null, //POST_NOTIFICATION + null, //NEIGHBORING_CELLS + null, //CALL_PHONE + null, //READ_SMS + null, //WRITE_SMS + null, //RECEIVE_SMS + null, //RECEIVE_EMERGECY_SMS + null, //RECEIVE_MMS + null, //RECEIVE_WAP_PUSH + null, //SEND_SMS + null, //READ_ICC_SMS + null, //WRITE_ICC_SMS + null, //WRITE_SETTINGS + new RestrictionBypass(true, false), //SYSTEM_ALERT_WINDOW + null, //ACCESS_NOTIFICATIONS + null, //CAMERA + new RestrictionBypass(false, true), //RECORD_AUDIO + null, //PLAY_AUDIO + null, //READ_CLIPBOARD + null, //WRITE_CLIPBOARD + null, //TAKE_MEDIA_BUTTONS + null, //TAKE_AUDIO_FOCUS + null, //AUDIO_MASTER_VOLUME + null, //AUDIO_VOICE_VOLUME + null, //AUDIO_RING_VOLUME + null, //AUDIO_MEDIA_VOLUME + null, //AUDIO_ALARM_VOLUME + null, //AUDIO_NOTIFICATION_VOLUME + null, //AUDIO_BLUETOOTH_VOLUME + null, //WAKE_LOCK + null, //MONITOR_LOCATION + null, //MONITOR_HIGH_POWER_LOCATION + null, //GET_USAGE_STATS + null, //MUTE_MICROPHONE + new RestrictionBypass(true, false), //TOAST_WINDOW + null, //PROJECT_MEDIA + null, //ACTIVATE_VPN + null, //WALLPAPER + null, //ASSIST_STRUCTURE + null, //ASSIST_SCREENSHOT + null, //READ_PHONE_STATE + null, //ADD_VOICEMAIL + null, // USE_SIP + null, // PROCESS_OUTGOING_CALLS + null, // USE_FINGERPRINT + null, // BODY_SENSORS + null, // READ_CELL_BROADCASTS + null, // MOCK_LOCATION + null, // READ_EXTERNAL_STORAGE + null, // WRITE_EXTERNAL_STORAGE + null, // TURN_ON_SCREEN + null, // GET_ACCOUNTS + null, // RUN_IN_BACKGROUND + null, // AUDIO_ACCESSIBILITY_VOLUME + null, // READ_PHONE_NUMBERS + null, // REQUEST_INSTALL_PACKAGES + null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE + null, // INSTANT_APP_START_FOREGROUND + null, // ANSWER_PHONE_CALLS + null, // OP_RUN_ANY_IN_BACKGROUND + null, // OP_CHANGE_WIFI_STATE + null, // OP_REQUEST_DELETE_PACKAGES + null, // OP_BIND_ACCESSIBILITY_SERVICE + null, // ACCEPT_HANDOVER + null, // MANAGE_IPSEC_HANDOVERS + null, // START_FOREGROUND + new RestrictionBypass(true, false), // BLUETOOTH_SCAN + null, // USE_BIOMETRIC + null, // ACTIVITY_RECOGNITION + null, // SMS_FINANCIAL_TRANSACTIONS + null, // READ_MEDIA_AUDIO + null, // WRITE_MEDIA_AUDIO + null, // READ_MEDIA_VIDEO + null, // WRITE_MEDIA_VIDEO + null, // READ_MEDIA_IMAGES + null, // WRITE_MEDIA_IMAGES + null, // LEGACY_STORAGE + null, // ACCESS_ACCESSIBILITY + null, // READ_DEVICE_IDENTIFIERS + null, // ACCESS_MEDIA_LOCATION + null, // QUERY_ALL_PACKAGES + null, // MANAGE_EXTERNAL_STORAGE + null, // INTERACT_ACROSS_PROFILES + null, // ACTIVATE_PLATFORM_VPN + null, // LOADER_USAGE_STATS + null, // ACCESS_CALL_AUDIO + null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED }; /** @@ -2178,6 +2193,7 @@ public class AppOpsManager { AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS AppOpsManager.MODE_DEFAULT, // ACCESS_CALL_AUDIO + AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED }; /** @@ -2285,6 +2301,7 @@ public class AppOpsManager { false, // ACTIVATE_PLATFORM_VPN false, // LOADER_USAGE_STATS false, // ACCESS_CALL_AUDIO + false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED }; /** @@ -2468,11 +2485,11 @@ public class AppOpsManager { } /** - * Retrieve whether the op allows the system (and system ui) to - * bypass the user restriction. + * Retrieve whether the op allows to bypass the user restriction. + * * @hide */ - public static boolean opAllowSystemBypassRestriction(int op) { + public static RestrictionBypass opAllowSystemBypassRestriction(int op) { return sOpAllowSystemRestrictionBypass[op]; } @@ -2519,6 +2536,29 @@ public class AppOpsManager { } /** + * When to not enforce {@link #setUserRestriction restrictions}. + * + * @hide + */ + public static class RestrictionBypass { + /** Does the app need to be privileged to bypass the restriction */ + public boolean isPrivileged; + + /** + * Does the app need to have the EXEMPT_FROM_AUDIO_RESTRICTIONS permission to bypass the + * restriction + */ + public boolean isRecordAudioRestrictionExcept; + + public RestrictionBypass(boolean isPrivileged, boolean isRecordAudioRestrictionExcept) { + this.isPrivileged = isPrivileged; + this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept; + } + + public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(true, true); + } + + /** * Class holding all of the operation information associated with an app. * @hide */ @@ -7783,8 +7823,8 @@ public class AppOpsManager { */ private void collectNotedOpForSelf(int op, @Nullable String featureId) { synchronized (sLock) { - if (sNotedAppOpsCollector != null) { - sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId)); + if (sOnOpNotedCallback != null) { + sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, featureId)); } } sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId)); @@ -7933,8 +7973,8 @@ public class AppOpsManager { synchronized (sLock) { for (int code = notedAppOps.nextSetBit(0); code != -1; code = notedAppOps.nextSetBit(code + 1)) { - if (sNotedAppOpsCollector != null) { - sNotedAppOpsCollector.onNoted(new SyncNotedAppOp(code, featureId)); + if (sOnOpNotedCallback != null) { + sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, featureId)); } } } @@ -7947,33 +7987,45 @@ public class AppOpsManager { } /** - * Register a new {@link AppOpsCollector}. + * Set a new {@link OnOpNotedCallback}. * - * <p>There can only ever be one collector per process. If there currently is a collector - * registered, it will be unregistered. + * <p>There can only ever be one collector per process. If there currently is another callback + * set, this will fail. * - * <p><b>Only appops related to dangerous permissions are collected.</b> + * @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code + * null} to unset + * @param callback listener to set, {@code null} to unset * - * @param collector The collector to set or {@code null} to unregister. + * @throws IllegalStateException If another callback is already registered */ - public void setNotedAppOpsCollector(@Nullable AppOpsCollector collector) { + public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor, + @Nullable OnOpNotedCallback callback) { + Preconditions.checkState((callback == null) == (asyncExecutor == null)); + synchronized (sLock) { - if (sNotedAppOpsCollector != null) { + if (callback == null) { + Preconditions.checkState(sOnOpNotedCallback != null, + "No callback is currently registered"); + try { mService.stopWatchingAsyncNoted(mContext.getPackageName(), - sNotedAppOpsCollector.mAsyncCb); + sOnOpNotedCallback.mAsyncCb); } catch (RemoteException e) { e.rethrowFromSystemServer(); } - } - sNotedAppOpsCollector = collector; + sOnOpNotedCallback = null; + } else { + Preconditions.checkState(sOnOpNotedCallback == null, + "Another callback is already registered"); + + callback.mAsyncExecutor = asyncExecutor; + sOnOpNotedCallback = callback; - if (sNotedAppOpsCollector != null) { List<AsyncNotedAppOp> missedAsyncOps = null; try { mService.startWatchingAsyncNoted(mContext.getPackageName(), - sNotedAppOpsCollector.mAsyncCb); + sOnOpNotedCallback.mAsyncCb); missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName()); } catch (RemoteException e) { e.rethrowFromSystemServer(); @@ -7983,10 +8035,9 @@ public class AppOpsManager { int numMissedAsyncOps = missedAsyncOps.size(); for (int i = 0; i < numMissedAsyncOps; i++) { final AsyncNotedAppOp asyncNotedAppOp = missedAsyncOps.get(i); - if (sNotedAppOpsCollector != null) { - sNotedAppOpsCollector.getAsyncNotedExecutor().execute( - () -> sNotedAppOpsCollector.onAsyncNoted( - asyncNotedAppOp)); + if (sOnOpNotedCallback != null) { + sOnOpNotedCallback.getAsyncNotedExecutor().execute( + () -> sOnOpNotedCallback.onAsyncNoted(asyncNotedAppOp)); } } } @@ -7994,27 +8045,50 @@ public class AppOpsManager { } } + // TODO moltmann: Remove + /** + * Will be removed before R ships, leave it just to not break apps immediately. + * + * @removed + * + * @hide + */ + @SystemApi + @Deprecated + public void setNotedAppOpsCollector(@Nullable AppOpsCollector collector) { + synchronized (sLock) { + if (collector != null) { + if (isListeningForOpNoted()) { + setOnOpNotedCallback(null, null); + } + setOnOpNotedCallback(new HandlerExecutor(Handler.getMain()), collector); + } else if (sOnOpNotedCallback != null) { + setOnOpNotedCallback(null, null); + } + } + } + /** * @return {@code true} iff the process currently is currently collecting noted appops. * - * @see #setNotedAppOpsCollector(AppOpsCollector) + * @see #setOnOpNotedCallback * * @hide */ - public static boolean isCollectingNotedAppOps() { - return sNotedAppOpsCollector != null; + public static boolean isListeningForOpNoted() { + return sOnOpNotedCallback != null; } /** - * Callback an app can {@link #setNotedAppOpsCollector register} to monitor the app-ops the + * Callback an app can {@link #setOnOpNotedCallback set} to monitor the app-ops the * system has tracked for it. I.e. each time any app calls {@link #noteOp} or {@link #startOp} - * one of the callback methods of this object is called. + * one of a method of this object is called. * - * <p><b>There will be a callback for all app-ops related to runtime permissions, but not + * <p><b>There will be a call for all app-ops related to runtime permissions, but not * necessarily for all other app-ops. * * <pre> - * setNotedAppOpsCollector(new AppOpsCollector() { + * setOnOpNotedCallback(getMainExecutor(), new OnOpNotedCallback() { * ArraySet<Pair<String, String>> opsNotedForThisProcess = new ArraySet<>(); * * private synchronized void addAccess(String op, String accessLocation) { @@ -8039,24 +8113,36 @@ public class AppOpsManager { * }); * </pre> * - * @see #setNotedAppOpsCollector + * @see #setOnOpNotedCallback */ - public abstract static class AppOpsCollector { + public abstract static class OnOpNotedCallback { + private @NonNull Executor mAsyncExecutor; + /** Callback registered with the system. This will receive the async notes ops */ private final IAppOpsAsyncNotedCallback mAsyncCb = new IAppOpsAsyncNotedCallback.Stub() { @Override public void opNoted(AsyncNotedAppOp op) { Objects.requireNonNull(op); - getAsyncNotedExecutor().execute(() -> onAsyncNoted(op)); + long token = Binder.clearCallingIdentity(); + try { + getAsyncNotedExecutor().execute(() -> onAsyncNoted(op)); + } finally { + Binder.restoreCallingIdentity(token); + } } }; + // TODO moltmann: Remove /** + * Will be removed before R ships. + * * @return The executor for the system to use when calling {@link #onAsyncNoted}. + * + * @hide */ - public @NonNull Executor getAsyncNotedExecutor() { - return new HandlerExecutor(Handler.getMain()); + protected @NonNull Executor getAsyncNotedExecutor() { + return mAsyncExecutor; } /** @@ -8066,7 +8152,7 @@ public class AppOpsManager { * <p>Called on the calling thread before the API returns. This allows the app to e.g. * collect stack traces to figure out where the access came from. * - * @param op The op noted + * @param op op noted */ public abstract void onNoted(@NonNull SyncNotedAppOp op); @@ -8076,7 +8162,7 @@ public class AppOpsManager { * <p>This is very similar to {@link #onNoted} only that the tracking was not caused by the * API provider in a separate process, but by one in the app's own process. * - * @param op The op noted + * @param op op noted */ public abstract void onSelfNoted(@NonNull SyncNotedAppOp op); @@ -8088,14 +8174,30 @@ public class AppOpsManager { * guaranteed. Due to how async calls work in Android this might even be delivered slightly * before the private data is delivered to the app. * - * <p>If the app is not running or no {@link AppOpsCollector} is registered a small amount - * of noted app-ops are buffered and then delivered as soon as a collector is registered. + * <p>If the app is not running or no {@link OnOpNotedCallback} is registered a small amount + * of noted app-ops are buffered and then delivered as soon as a listener is registered. * - * @param asyncOp The op noted + * @param asyncOp op noted */ public abstract void onAsyncNoted(@NonNull AsyncNotedAppOp asyncOp); } + // TODO moltmann: Remove + /** + * Will be removed before R ships, leave it just to not break apps immediately. + * + * @removed + * + * @hide + */ + @SystemApi + @Deprecated + public abstract static class AppOpsCollector extends OnOpNotedCallback { + public @NonNull Executor getAsyncNotedExecutor() { + return new HandlerExecutor(Handler.getMain()); + } + }; + /** * Generate a stack trace used for noted app-ops logging. * diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java index 6b1afdad82df..4d955dbe8703 100644 --- a/core/java/android/app/AsyncNotedAppOp.java +++ b/core/java/android/app/AsyncNotedAppOp.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.CurrentTimeMillisLong; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,13 +24,15 @@ import android.os.Parcelable; import com.android.internal.annotations.Immutable; import com.android.internal.util.DataClass; +import com.android.internal.util.Preconditions; /** * When an {@link AppOpsManager#noteOp(String, int, String, String, String) app-op is noted} and the - * app the app-op is noted for has a {@link AppOpsManager.AppOpsCollector} registered the note-event - * needs to be delivered to the collector. Usually this is done via an {@link SyncNotedAppOp}, but - * in some cases this is not possible. In this case an {@link AsyncNotedAppOp} is send to the system - * server and then forwarded to the {@link AppOpsManager.AppOpsCollector} in the app. + * app the app-op is noted for has a {@link AppOpsManager.OnOpNotedCallback} registered the + * note-event needs to be delivered to the callback. Usually this is done via an + * {@link SyncNotedAppOp}, but in some cases this is not possible. In this case an + * {@link AsyncNotedAppOp} is send to the system server and then forwarded to the + * {@link AppOpsManager.OnOpNotedCallback} in the app. */ @Immutable @DataClass(genEqualsHashCode = true, @@ -40,7 +43,7 @@ import com.android.internal.util.DataClass; @DataClass.Suppress({"getOpCode"}) public final class AsyncNotedAppOp implements Parcelable { /** Op that was noted */ - private final @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int mOpCode; + private final @IntRange(from = 0) int mOpCode; /** Uid that noted the op */ private final @IntRange(from = 0) int mNotingUid; @@ -52,7 +55,7 @@ public final class AsyncNotedAppOp implements Parcelable { private final @NonNull String mMessage; /** Milliseconds since epoch when the op was noted */ - private final @IntRange(from = 0) long mTime; + private final @CurrentTimeMillisLong long mTime; /** * @return Op that was noted. @@ -61,9 +64,15 @@ public final class AsyncNotedAppOp implements Parcelable { return AppOpsManager.opToPublicName(mOpCode); } + //TODO eugenesusla: support inlinable expressions in annotation params of @DataClass members to + // allow validating via @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) + private void onConstructed() { + Preconditions.checkArgumentInRange(mOpCode, 0, AppOpsManager._NUM_OP - 1, "opCode"); + } + - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -93,16 +102,15 @@ public final class AsyncNotedAppOp implements Parcelable { */ @DataClass.Generated.Member public AsyncNotedAppOp( - @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode, + @IntRange(from = 0) int opCode, @IntRange(from = 0) int notingUid, @Nullable String featureId, @NonNull String message, - @IntRange(from = 0) long time) { + @CurrentTimeMillisLong long time) { this.mOpCode = opCode; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mOpCode, - "from", 0, - "to", AppOpsManager._NUM_OP - 1); + "from", 0); this.mNotingUid = notingUid; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mNotingUid, @@ -113,10 +121,9 @@ public final class AsyncNotedAppOp implements Parcelable { NonNull.class, null, mMessage); this.mTime = time; com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mTime, - "from", 0); + CurrentTimeMillisLong.class, null, mTime); - // onConstructed(); // You can define this method to get a callback + onConstructed(); } /** @@ -147,7 +154,7 @@ public final class AsyncNotedAppOp implements Parcelable { * Milliseconds since epoch when the op was noted */ @DataClass.Generated.Member - public @IntRange(from = 0) long getTime() { + public @CurrentTimeMillisLong long getTime() { return mTime; } @@ -223,8 +230,7 @@ public final class AsyncNotedAppOp implements Parcelable { this.mOpCode = opCode; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mOpCode, - "from", 0, - "to", AppOpsManager._NUM_OP - 1); + "from", 0); this.mNotingUid = notingUid; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mNotingUid, @@ -235,10 +241,9 @@ public final class AsyncNotedAppOp implements Parcelable { NonNull.class, null, mMessage); this.mTime = time; com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mTime, - "from", 0); + CurrentTimeMillisLong.class, null, mTime); - // onConstructed(); // You can define this method to get a callback + onConstructed(); } @DataClass.Generated.Member @@ -256,10 +261,10 @@ public final class AsyncNotedAppOp implements Parcelable { }; @DataClass.Generated( - time = 1581728574427L, - codegenVersion = "1.0.14", + time = 1583866178330L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L, to=96L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.IntRange(from=0L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate void onConstructed()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 28b28dad5b82..145d5139cdc2 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -37,13 +37,15 @@ oneway interface ITaskStackListener { /** * Called whenever IActivityManager.startActivity is called on an activity that is already - * running in the pinned stack and the activity is not actually started, but the task is either - * brought to the front or a new Intent is delivered to it. + * running, but the task is either brought to the front or a new Intent is delivered to it. * + * @param task information about the task the activity was relaunched into + * @param homeVisible whether or not the home task is visible * @param clearedTask whether or not the launch activity also cleared the task as a part of * starting */ - void onPinnedActivityRestartAttempt(boolean clearedTask); + void onActivityRestartAttempt(in ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask); /** * Called when we launched an activity that we forced to be resizable. diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 3f2ec4487668..94b237cd1f5b 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -134,6 +134,7 @@ public final class NotificationChannel implements Parcelable { * @hide */ @SystemApi + @TestApi public static final int USER_LOCKED_SOUND = 0x00000020; /** @@ -331,6 +332,7 @@ public final class NotificationChannel implements Parcelable { /** * @hide */ + @TestApi public void lockFields(int field) { mUserLockedFields |= field; } diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java index aa11b9510c94..13b90ca6ced1 100644 --- a/core/java/android/app/SyncNotedAppOp.java +++ b/core/java/android/app/SyncNotedAppOp.java @@ -28,9 +28,9 @@ import com.android.internal.util.DataClass; * Description of an app-op that was noted for the current process. * * <p>This is either delivered after a - * {@link AppOpsManager.AppOpsCollector#onNoted(SyncNotedAppOp) two way binder call} or + * {@link AppOpsManager.OnOpNotedCallback#onNoted(SyncNotedAppOp) two way binder call} or * when the app - * {@link AppOpsManager.AppOpsCollector#onSelfNoted(SyncNotedAppOp) notes an app-op for + * {@link AppOpsManager.OnOpNotedCallback#onSelfNoted(SyncNotedAppOp) notes an app-op for * itself}. */ @Immutable diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index b892b8e51c88..93772de0ba30 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -16,6 +16,7 @@ package android.app; +import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.TaskSnapshot; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -53,7 +54,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage - public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException { + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) throws RemoteException { } @Override @@ -68,14 +70,14 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onActivityLaunchOnSecondaryDisplayFailed(ActivityManager.RunningTaskInfo taskInfo, + public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo, int requestedDisplayId) throws RemoteException { onActivityLaunchOnSecondaryDisplayFailed(); } /** * @deprecated see {@link - * #onActivityLaunchOnSecondaryDisplayFailed(ActivityManager.RunningTaskInfo, int)} + * #onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo, int)} */ @Deprecated @UnsupportedAppUsage @@ -84,7 +86,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage - public void onActivityLaunchOnSecondaryDisplayRerouted(ActivityManager.RunningTaskInfo taskInfo, + public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo, int requestedDisplayId) throws RemoteException { } @@ -98,13 +100,13 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) + public void onTaskMovedToFront(RunningTaskInfo taskInfo) throws RemoteException { onTaskMovedToFront(taskInfo.taskId); } /** - * @deprecated see {@link #onTaskMovedToFront(ActivityManager.RunningTaskInfo)} + * @deprecated see {@link #onTaskMovedToFront(RunningTaskInfo)} */ @Deprecated @UnsupportedAppUsage @@ -112,26 +114,26 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo) + public void onTaskRemovalStarted(RunningTaskInfo taskInfo) throws RemoteException { onTaskRemovalStarted(taskInfo.taskId); } /** - * @deprecated see {@link #onTaskRemovalStarted(ActivityManager.RunningTaskInfo)} + * @deprecated see {@link #onTaskRemovalStarted(RunningTaskInfo)} */ @Deprecated public void onTaskRemovalStarted(int taskId) throws RemoteException { } @Override - public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo) + public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) throws RemoteException { onTaskDescriptionChanged(taskInfo.taskId, taskInfo.taskDescription); } /** - * @deprecated see {@link #onTaskDescriptionChanged(ActivityManager.RunningTaskInfo)} + * @deprecated see {@link #onTaskDescriptionChanged(RunningTaskInfo)} */ @Deprecated public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td) @@ -166,7 +168,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) throws RemoteException { } diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java index 568456270809..878993ebcd19 100644 --- a/core/java/android/app/WindowContext.java +++ b/core/java/android/app/WindowContext.java @@ -60,7 +60,6 @@ public class WindowContext extends ContextWrapper { mWms = WindowManagerGlobal.getWindowManagerService(); mToken = new WindowTokenClient(); - final ContextImpl contextImpl = createBaseWindowContext(base, mToken); attachBaseContext(contextImpl); contextImpl.setOuterContext(this); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index b219394ddfa9..c532279676a0 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8781,18 +8781,20 @@ public class DevicePolicyManager { } /** + * This method is mostly deprecated. + * Most of the settings that still have an effect have dedicated setter methods or user + * restrictions. See individual settings for details. + * <p> * Called by device owner to update {@link android.provider.Settings.Global} settings. * Validation that the value of the setting is in the correct form for the setting type should * be performed by the caller. * <p> * The settings that can be updated with this method are: * <ul> - * <li>{@link android.provider.Settings.Global#ADB_ENABLED}</li> - * <li>{@link android.provider.Settings.Global#AUTO_TIME}</li> - * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE}</li> - * <li>{@link android.provider.Settings.Global#DATA_ROAMING}</li> + * <li>{@link android.provider.Settings.Global#ADB_ENABLED} : use + * {@link UserManager#DISALLOW_DEBUGGING_FEATURES} instead to restrict users from enabling + * debugging features and this setting to turn adb on.</li> * <li>{@link android.provider.Settings.Global#USB_MASS_STORAGE_ENABLED}</li> - * <li>{@link android.provider.Settings.Global#WIFI_SLEEP_POLICY}</li> * <li>{@link android.provider.Settings.Global#STAY_ON_WHILE_PLUGGED_IN} This setting is only * available from {@link android.os.Build.VERSION_CODES#M} onwards and can only be set if * {@link #setMaximumTimeToLock} is not used to set a timeout.</li> @@ -8800,6 +8802,16 @@ public class DevicePolicyManager { * setting is only available from {@link android.os.Build.VERSION_CODES#M} onwards.</li> * </ul> * <p> + * The following settings used to be supported, but can be controlled in other ways: + * <ul> + * <li>{@link android.provider.Settings.Global#AUTO_TIME} : Use {@link #setAutoTime} and + * {@link UserManager#DISALLOW_CONFIG_DATE_TIME} instead.</li> + * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE} : Use {@link #setAutoTimeZone} + * and {@link UserManager#DISALLOW_CONFIG_DATE_TIME} instead.</li> + * <li>{@link android.provider.Settings.Global#DATA_ROAMING} : Use + * {@link UserManager#DISALLOW_DATA_ROAMING} instead.</li> + * </ul> + * <p> * Changing the following settings has no effect as of {@link android.os.Build.VERSION_CODES#M}: * <ul> * <li>{@link android.provider.Settings.Global#BLUETOOTH_ON}. Use @@ -8811,6 +8823,7 @@ public class DevicePolicyManager { * <li>{@link android.provider.Settings.Global#NETWORK_PREFERENCE}</li> * <li>{@link android.provider.Settings.Global#WIFI_ON}. Use * {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} instead.</li> + * <li>{@link android.provider.Settings.Global#WIFI_SLEEP_POLICY}. No longer has effect.</li> * </ul> * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. @@ -8989,6 +9002,11 @@ public class DevicePolicyManager { } /** + * This method is mostly deprecated. + * Most of the settings that still have an effect have dedicated setter methods + * (e.g. {@link #setLocationEnabled}) or user restrictions. + * <p> + * * Called by profile or device owners to update {@link android.provider.Settings.Secure} * settings. Validation that the value of the setting is in the correct form for the setting * type should be performed by the caller. @@ -9001,7 +9019,7 @@ public class DevicePolicyManager { * <p> * A device owner can additionally update the following settings: * <ul> - * <li>{@link android.provider.Settings.Secure#LOCATION_MODE}</li> + * <li>{@link android.provider.Settings.Secure#LOCATION_MODE}, but see note below.</li> * </ul> * * <strong>Note: Starting from Android O, apps should no longer call this method with the @@ -10355,19 +10373,23 @@ public class DevicePolicyManager { } /** - * Indicates the entity that controls the device or profile owner. Two users/profiles are - * affiliated if the set of ids set by their device or profile owners intersect. + * Indicates the entity that controls the device. Two users are + * affiliated if the set of ids set by the device owner and the admin of the secondary user. * - * <p>A user/profile that is affiliated with the device owner user is considered to be + * <p>A user that is affiliated with the device owner user is considered to be * affiliated with the device. * * <p><strong>Note:</strong> Features that depend on user affiliation (such as security logging - * or {@link #bindDeviceAdminServiceAsUser}) won't be available when a secondary user or profile + * or {@link #bindDeviceAdminServiceAsUser}) won't be available when a secondary user * is created, until it becomes affiliated. Therefore it is recommended that the appropriate - * affiliation ids are set by its profile owner as soon as possible after the user/profile is + * affiliation ids are set by its owner as soon as possible after the user is * created. + * <p> + * Note: This method used to be available for affiliating device owner and profile + * owner. However, since Android 11, this combination is not possible. This method is now + * only useful for affiliating the primary user with managed secondary users. * - * @param admin Which profile or device owner this request is associated with. + * @param admin Which device owner, or owner of secondary user, this request is associated with. * @param ids A set of opaque non-empty affiliation ids. * * @throws IllegalArgumentException if {@code ids} is null or contains an empty string. @@ -10399,10 +10421,10 @@ public class DevicePolicyManager { } /** - * Returns whether this user/profile is affiliated with the device. + * Returns whether this user is affiliated with the device. * <p> * By definition, the user that the device owner runs on is always affiliated with the device. - * Any other user/profile is considered affiliated with the device if the set specified by its + * Any other user is considered affiliated with the device if the set specified by its * profile owner via {@link #setAffiliationIds} intersects with the device owner's. * @see #setAffiliationIds */ @@ -10706,14 +10728,18 @@ public class DevicePolicyManager { } /** - * Called by a device owner to bind to a service from a profile owner or vice versa. - * See {@link #getBindDeviceAdminTargetUsers} for a definition of which - * device/profile owners are allowed to bind to services of another profile/device owner. + * Called by a device owner to bind to a service from a secondary managed user or vice versa. + * See {@link #getBindDeviceAdminTargetUsers} for the pre-requirements of a + * device owner to bind to services of another managed user. * <p> * The service must be protected by {@link android.Manifest.permission#BIND_DEVICE_ADMIN}. * Note that the {@link Context} used to obtain this * {@link DevicePolicyManager} instance via {@link Context#getSystemService(Class)} will be used * to bind to the {@link android.app.Service}. + * <p> + * Note: This method used to be available for communication between device owner and profile + * owner. However, since Android 11, this combination is not possible. This method is now + * only useful for communication between device owner and managed secondary users. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param serviceIntent Identifies the service to connect to. The Intent must specify either an @@ -10751,14 +10777,11 @@ public class DevicePolicyManager { } /** - * Returns the list of target users that the calling device or profile owner can use when - * calling {@link #bindDeviceAdminServiceAsUser}. + * Returns the list of target users that the calling device owner or owner of secondary user + * can use when calling {@link #bindDeviceAdminServiceAsUser}. * <p> - * A device owner can bind to a service from a profile owner and vice versa, provided that: - * <ul> - * <li>Both belong to the same package name. - * <li>Both users are affiliated. See {@link #setAffiliationIds}. - * </ul> + * A device owner can bind to a service from a secondary managed user and vice versa, provided + * that both users are affiliated. See {@link #setAffiliationIds}. */ public @NonNull List<UserHandle> getBindDeviceAdminTargetUsers(@NonNull ComponentName admin) { throwIfParentInstance("getBindDeviceAdminTargetUsers"); diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index ccd8199b8373..0d461f5fa32c 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -174,7 +174,7 @@ public class AppWidgetManager { public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE"; /** - * An intent extra that contains one appWidgetId. + * An intent extra (int) that contains one appWidgetId. * <p> * The value will be an int that can be retrieved like this: * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/AppWidgetHostActivity.java getExtra_EXTRA_APPWIDGET_ID} @@ -182,7 +182,7 @@ public class AppWidgetManager { public static final String EXTRA_APPWIDGET_ID = "appWidgetId"; /** - * A bundle extra that contains whether or not an app has finished restoring a widget. + * A bundle extra (boolean) that contains whether or not an app has finished restoring a widget. * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its * widgets followed by calling {@link #updateAppWidget} to update the views. * @@ -192,22 +192,26 @@ public class AppWidgetManager { /** - * A bundle extra that contains the lower bound on the current width, in dips, of a widget instance. + * A bundle extra (int) that contains the lower bound on the current width, in dips, of a + * widget instance. */ public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth"; /** - * A bundle extra that contains the lower bound on the current height, in dips, of a widget instance. + * A bundle extra (int) that contains the lower bound on the current height, in dips, of a + * widget instance. */ public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight"; /** - * A bundle extra that contains the upper bound on the current width, in dips, of a widget instance. + * A bundle extra (int) that contains the upper bound on the current width, in dips, of a + * widget instance. */ public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth"; /** - * A bundle extra that contains the upper bound on the current width, in dips, of a widget instance. + * A bundle extra (int) that contains the upper bound on the current width, in dips, of a + * widget instance. */ public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight"; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 66bfcbd27ca6..6ae68fcad6f4 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1229,10 +1229,11 @@ public final class BluetoothAdapter { public boolean factoryReset() { try { mServiceLock.readLock().lock(); - if (mService != null) { - return mService.factoryReset(); + if (mService != null && mService.factoryReset() + && mManagerService != null && mManagerService.onFactoryReset()) { + return true; } - Log.e(TAG, "factoryReset(): IBluetooth Service is null"); + Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); SystemProperties.set("persist.bluetooth.factoryreset", "true"); } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/core/java/android/companion/WifiDeviceFilter.java b/core/java/android/companion/WifiDeviceFilter.java index 58bf874efb41..8b48f4c46977 100644 --- a/core/java/android/companion/WifiDeviceFilter.java +++ b/core/java/android/companion/WifiDeviceFilter.java @@ -51,6 +51,7 @@ public final class WifiDeviceFilter implements DeviceFilter<ScanResult> { * expression will be shown */ @DataClass.ParcelWith(Parcelling.BuiltIn.ForPattern.class) + @DataClass.MaySetToNull private @Nullable Pattern mNamePattern = null; /** @@ -86,7 +87,7 @@ public final class WifiDeviceFilter implements DeviceFilter<ScanResult> { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -273,7 +274,7 @@ public final class WifiDeviceFilter implements DeviceFilter<ScanResult> { * If set, only devices with BSSID matching the given one will be shown */ @DataClass.Generated.Member - public @NonNull Builder setBssid(@Nullable MacAddress value) { + public @NonNull Builder setBssid(@NonNull MacAddress value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; mBssid = value; @@ -322,11 +323,15 @@ public final class WifiDeviceFilter implements DeviceFilter<ScanResult> { } @DataClass.Generated( - time = 1571960300742L, - codegenVersion = "1.0.11", + time = 1582688421965L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/companion/WifiDeviceFilter.java", - inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.Nullable java.util.regex.Pattern mNamePattern\nprivate @android.annotation.Nullable android.net.MacAddress mBssid\nprivate @android.annotation.NonNull android.net.MacAddress mBssidMask\npublic @java.lang.Override boolean matches(android.net.wifi.ScanResult)\npublic @java.lang.Override java.lang.String getDeviceDisplayName(android.net.wifi.ScanResult)\npublic @java.lang.Override int getMediumType()\nclass WifiDeviceFilter extends java.lang.Object implements [android.companion.DeviceFilter<android.net.wifi.ScanResult>]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=false, genBuilder=true, genEqualsHashCode=true, genHiddenGetters=true)") + inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable java.util.regex.Pattern mNamePattern\nprivate @android.annotation.Nullable android.net.MacAddress mBssid\nprivate @android.annotation.NonNull android.net.MacAddress mBssidMask\npublic @java.lang.Override boolean matches(android.net.wifi.ScanResult)\npublic @java.lang.Override java.lang.String getDeviceDisplayName(android.net.wifi.ScanResult)\npublic @java.lang.Override int getMediumType()\nclass WifiDeviceFilter extends java.lang.Object implements [android.companion.DeviceFilter<android.net.wifi.ScanResult>]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=false, genBuilder=true, genEqualsHashCode=true, genHiddenGetters=true)") @Deprecated private void __metadata() {} + + //@formatter:on + // End of generated code + } diff --git a/core/java/android/content/ApexContext.java b/core/java/android/content/ApexEnvironment.java index fe5cedca4654..b4cc3c2bb156 100644 --- a/core/java/android/content/ApexContext.java +++ b/core/java/android/content/ApexEnvironment.java @@ -30,29 +30,29 @@ import java.util.Objects; * @hide */ @SystemApi -public class ApexContext { +public class ApexEnvironment { private static final String APEX_DATA = "apexdata"; /** - * Returns an ApexContext instance for the APEX with the provided {@code apexModuleName}. + * Returns an ApexEnvironment instance for the APEX with the provided {@code apexModuleName}. * - * <p>To preserve the safety and integrity of APEX modules, you must only obtain the ApexContext - * for your specific APEX, and you <em>must never</em> attempt to obtain an ApexContext for - * another APEX. Any coordination between APEXs must be performed through well-defined - * interfaces; attempting to directly read or write raw files belonging to another APEX will - * violate the hermetic storage requirements placed upon each module. + * <p>To preserve the safety and integrity of APEX modules, you must only obtain the + * ApexEnvironment for your specific APEX, and you <em>must never</em> attempt to obtain an + * ApexEnvironment for another APEX. Any coordination between APEXs must be performed through + * well-defined interfaces; attempting to directly read or write raw files belonging to another + * APEX will violate the hermetic storage requirements placed upon each module. */ @NonNull - public static ApexContext getApexContext(@NonNull String apexModuleName) { + public static ApexEnvironment getApexEnvironment(@NonNull String apexModuleName) { Objects.requireNonNull(apexModuleName, "apexModuleName cannot be null"); //TODO(b/141148175): Check that apexModuleName is an actual APEX name - return new ApexContext(apexModuleName); + return new ApexEnvironment(apexModuleName); } private final String mApexModuleName; - private ApexContext(String apexModuleName) { + private ApexEnvironment(String apexModuleName) { mApexModuleName = apexModuleName; } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index ae786aa30ae0..31e1fc824ed2 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -23,6 +23,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; @@ -82,6 +83,7 @@ import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Random; @@ -2669,6 +2671,15 @@ public abstract class ContentResolver implements ContentInterface { ContentProvider.getUserIdFromUri(uri, mContext.getUserId())); } + /** @removed */ + @Deprecated + public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer, + @NotifyFlags int flags) { + final Collection<Uri> asCollection = new ArrayList<>(); + uris.forEach(asCollection::add); + notifyChange(asCollection, observer, flags); + } + /** * Notify registered observers that several rows have been updated. * <p> @@ -2693,7 +2704,7 @@ public abstract class ContentResolver implements ContentInterface { * @param flags Flags such as {@link #NOTIFY_SYNC_TO_NETWORK} or * {@link #NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS}. */ - public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer, + public void notifyChange(@NonNull Collection<Uri> uris, @Nullable ContentObserver observer, @NotifyFlags int flags) { Objects.requireNonNull(uris, "uris"); @@ -4014,6 +4025,10 @@ public abstract class ContentResolver implements ContentInterface { * @hide */ @SystemApi + @TestApi + // We can't accept an already-opened FD here, since these methods are + // rewriting actual filesystem paths + @SuppressLint("StreamFiles") public static @NonNull Uri decodeFromFile(@NonNull File file) { return translateDeprecatedDataPath(file.getAbsolutePath()); } @@ -4030,6 +4045,10 @@ public abstract class ContentResolver implements ContentInterface { * @hide */ @SystemApi + @TestApi + // We can't accept an already-opened FD here, since these methods are + // rewriting actual filesystem paths + @SuppressLint("StreamFiles") public static @NonNull File encodeToFile(@NonNull Uri uri) { return new File(translateDeprecatedDataPath(uri)); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index c6f6972bfc4d..4bb7346d6416 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1881,6 +1881,20 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MANAGE_PERMISSIONS"; /** + * Activity action: Launch UI to manage auto-revoke state. + * <p> + * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose + * auto-revoke state will be reviewed (mandatory). + * </p> + * <p> + * Output: Nothing. + * </p> + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_AUTO_REVOKE_PERMISSIONS = + "android.intent.action.AUTO_REVOKE_PERMISSIONS"; + + /** * Activity action: Launch UI to review permissions for an app. * The system uses this intent if permission review for apps not * supporting the new runtime permissions model is enabled. In diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 61128f287e0b..f19ab0105d5b 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -274,6 +274,9 @@ public class IntentFilter implements Parcelable { */ public static final String SCHEME_HTTPS = "https"; + /** The value to indicate a wildcard for incoming match arguments. */ + private static final String WILDCARD = "*"; + private int mPriority; @UnsupportedAppUsage private int mOrder; @@ -758,6 +761,17 @@ public class IntentFilter implements Parcelable { * @return True if the action is listed in the filter. */ public final boolean matchAction(String action) { + return matchAction(action, false); + } + + /** + * Variant of {@link #matchAction(String)} that allows a wildcard for the provided action. + * @param wildcardSupported if true, will allow action to use wildcards + */ + private boolean matchAction(String action, boolean wildcardSupported) { + if (wildcardSupported && !mActions.isEmpty() && WILDCARD.equals(action)) { + return true; + } return hasAction(action); } @@ -1120,20 +1134,33 @@ public class IntentFilter implements Parcelable { * {@link IntentFilter#MATCH_CATEGORY_HOST}. */ public int match(Uri data) { + return match(data, false); + } + + /** + * Variant of {@link #match(Uri)} that supports wildcards on the scheme, host and + * path of the provided {@link Uri} + * + * @param wildcardSupported if true, will allow parameters to use wildcards + * @hide + */ + public int match(Uri data, boolean wildcardSupported) { String host = data.getHost(); if (host == null) { return NO_MATCH_DATA; } if (false) Log.v("IntentFilter", "Match host " + host + ": " + mHost); - if (mWild) { - if (host.length() < mHost.length()) { + if (!wildcardSupported || !WILDCARD.equals(host)) { + if (mWild) { + if (host.length() < mHost.length()) { + return NO_MATCH_DATA; + } + host = host.substring(host.length() - mHost.length()); + } + if (host.compareToIgnoreCase(mHost) != 0) { return NO_MATCH_DATA; } - host = host.substring(host.length()-mHost.length()); - } - if (host.compareToIgnoreCase(mHost) != 0) { - return NO_MATCH_DATA; } if (mPort >= 0) { if (mPort != data.getPort()) { @@ -1207,9 +1234,21 @@ public class IntentFilter implements Parcelable { * filter. */ public final boolean hasDataSchemeSpecificPart(String data) { + return hasDataSchemeSpecificPart(data, false); + } + + /** + * Variant of {@link #hasDataSchemeSpecificPart(String)} that supports wildcards on the provided + * ssp. + * @param supportWildcards if true, will allow parameters to use wildcards + */ + private boolean hasDataSchemeSpecificPart(String data, boolean supportWildcards) { if (mDataSchemeSpecificParts == null) { return false; } + if (supportWildcards && WILDCARD.equals(data) && mDataSchemeSpecificParts.size() > 0) { + return true; + } final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size(); for (int i = 0; i < numDataSchemeSpecificParts; i++) { final PatternMatcher pe = mDataSchemeSpecificParts.get(i); @@ -1388,9 +1427,21 @@ public class IntentFilter implements Parcelable { * filter. */ public final boolean hasDataPath(String data) { + return hasDataPath(data, false); + } + + /** + * Variant of {@link #hasDataPath(String)} that supports wildcards on the provided scheme, host, + * and path. + * @param wildcardSupported if true, will allow parameters to use wildcards + */ + private boolean hasDataPath(String data, boolean wildcardSupported) { if (mDataPaths == null) { return false; } + if (wildcardSupported && WILDCARD.equals(data)) { + return true; + } final int numDataPaths = mDataPaths.size(); for (int i = 0; i < numDataPaths; i++) { final PatternMatcher pe = mDataPaths.get(i); @@ -1435,13 +1486,24 @@ public class IntentFilter implements Parcelable { * {@link #MATCH_CATEGORY_PORT}, {@link #NO_MATCH_DATA}. */ public final int matchDataAuthority(Uri data) { - if (mDataAuthorities == null || data == null) { + return matchDataAuthority(data, false); + } + + /** + * Variant of {@link #matchDataAuthority(Uri)} that allows wildcard matching of the provided + * authority. + * + * @param wildcardSupported if true, will allow parameters to use wildcards + * @hide + */ + public final int matchDataAuthority(Uri data, boolean wildcardSupported) { + if (data == null || mDataAuthorities == null) { return NO_MATCH_DATA; } final int numDataAuthorities = mDataAuthorities.size(); for (int i = 0; i < numDataAuthorities; i++) { final AuthorityEntry ae = mDataAuthorities.get(i); - int match = ae.match(data); + int match = ae.match(data, wildcardSupported); if (match >= 0) { return match; } @@ -1488,6 +1550,15 @@ public class IntentFilter implements Parcelable { * @see #match */ public final int matchData(String type, String scheme, Uri data) { + return matchData(type, scheme, data, false); + } + + /** + * Variant of {@link #matchData(String, String, Uri)} that allows wildcard matching + * of the provided type, scheme, host and path parameters. + * @param wildcardSupported if true, will allow parameters to use wildcards + */ + private int matchData(String type, String scheme, Uri data, boolean wildcardSupported) { final ArrayList<String> types = mDataTypes; final ArrayList<String> schemes = mDataSchemes; @@ -1499,7 +1570,8 @@ public class IntentFilter implements Parcelable { } if (schemes != null) { - if (schemes.contains(scheme != null ? scheme : "")) { + if (schemes.contains(scheme != null ? scheme : "") + || wildcardSupported && WILDCARD.equals(scheme)) { match = MATCH_CATEGORY_SCHEME; } else { return NO_MATCH_DATA; @@ -1507,19 +1579,19 @@ public class IntentFilter implements Parcelable { final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts; if (schemeSpecificParts != null && data != null) { - match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart()) + match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart(), wildcardSupported) ? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA; } if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) { // If there isn't any matching ssp, we need to match an authority. final ArrayList<AuthorityEntry> authorities = mDataAuthorities; if (authorities != null) { - int authMatch = matchDataAuthority(data); + int authMatch = matchDataAuthority(data, wildcardSupported); if (authMatch >= 0) { final ArrayList<PatternMatcher> paths = mDataPaths; if (paths == null) { match = authMatch; - } else if (hasDataPath(data.getPath())) { + } else if (hasDataPath(data.getPath(), wildcardSupported)) { match = MATCH_CATEGORY_PATH; } else { return NO_MATCH_DATA; @@ -1541,7 +1613,8 @@ public class IntentFilter implements Parcelable { // to force everyone to say they handle content: or file: URIs. if (scheme != null && !"".equals(scheme) && !"content".equals(scheme) - && !"file".equals(scheme)) { + && !"file".equals(scheme) + && !(wildcardSupported && WILDCARD.equals(scheme))) { return NO_MATCH_DATA; } } @@ -1701,13 +1774,23 @@ public class IntentFilter implements Parcelable { */ public final int match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag) { - if (action != null && !matchAction(action)) { + return match(action, type, scheme, data, categories, logTag, false /*supportWildcards*/); + } + + /** + * Variant of {@link #match(ContentResolver, Intent, boolean, String)} that supports wildcards + * in the action, type, scheme, host and path. + * @hide if true, will allow supported parameters to use wildcards to match this filter + */ + public final int match(String action, String type, String scheme, + Uri data, Set<String> categories, String logTag, boolean supportWildcards) { + if (action != null && !matchAction(action, supportWildcards)) { if (false) Log.v( logTag, "No matching action " + action + " for " + this); return NO_MATCH_ACTION; } - int dataMatch = matchData(type, scheme, data); + int dataMatch = matchData(type, scheme, data, supportWildcards); if (dataMatch < 0) { if (false) { if (dataMatch == NO_MATCH_TYPE) { diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java index c0fdcc900577..a45bf7930509 100644 --- a/core/java/android/content/pm/InstallSourceInfo.java +++ b/core/java/android/content/pm/InstallSourceInfo.java @@ -66,7 +66,18 @@ public final class InstallSourceInfo implements Parcelable { mInstallingPackageName = source.readString(); } - /** The name of the package that requested the installation, or null if not available. */ + /** + * The name of the package that requested the installation, or null if not available. + * + * This is normally the same as the installing package name. If the installing package name + * is changed, for example by calling + * {@link PackageManager#setInstallerPackageName(String, String)}, the initiating package name + * remains unchanged. It continues to identify the actual package that performed the install + * or update. + * <p> + * Null may be returned if the app was not installed by a package (e.g. a system app or an app + * installed via adb) or if the initiating package has itself been uninstalled. + */ @Nullable public String getInitiatingPackageName() { return mInitiatingPackageName; @@ -100,9 +111,11 @@ public final class InstallSourceInfo implements Parcelable { /** * The name of the package responsible for the installation (the installer of record), or null * if not available. - * Note that this may differ from the initiating package name and can be modified. - * - * @see PackageManager#setInstallerPackageName(String, String) + * Note that this may differ from the initiating package name and can be modified via + * {@link PackageManager#setInstallerPackageName(String, String)}. + * <p> + * Null may be returned if the app was not installed by a package (e.g. a system app or an app + * installed via adb) or if the installing package has itself been uninstalled. */ @Nullable public String getInstallingPackageName() { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 9b28cb5e88ab..7600a08a256c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2227,6 +2227,9 @@ public abstract class PackageManager { * <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is * supported.</li> * </ul> + * A subset of devices that support Vulkan 1.1 do so via software emulation. For more + * information, see + * <a href="{@docRoot}ndk/guides/graphics/design-notes">Vulkan Design Guidelines</a>. */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version"; @@ -3400,29 +3403,12 @@ public abstract class PackageManager { public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16; /** - * Permission flag: The permission is whitelisted to not be auto-revoked when app goes unused. - * - * @hide - */ - @SystemApi - public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 1 << 17; - - /** - * Permission flag: Whether {@link #FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED} state was set by - * user. - * - * @hide - */ - @SystemApi - public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 1 << 18; - - /** * Permission flag: Whether permission was revoked by auto-revoke. * * @hide */ @SystemApi - public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 20; + public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 17; /** * Permission flags: Reserved for use by the permission controller. @@ -3476,8 +3462,6 @@ public abstract class PackageManager { | FLAG_PERMISSION_GRANTED_BY_ROLE | FLAG_PERMISSION_REVOKED_COMPAT | FLAG_PERMISSION_ONE_TIME - | FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED - | FLAG_PERMISSION_AUTO_REVOKE_USER_SET | FLAG_PERMISSION_AUTO_REVOKED; /** @@ -4302,8 +4286,6 @@ public abstract class PackageManager { FLAG_PERMISSION_GRANTED_BY_ROLE, FLAG_PERMISSION_REVOKED_COMPAT, FLAG_PERMISSION_ONE_TIME, - FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, - FLAG_PERMISSION_AUTO_REVOKE_USER_SET, FLAG_PERMISSION_AUTO_REVOKED }) @Retention(RetentionPolicy.SOURCE) @@ -7471,8 +7453,6 @@ public abstract class PackageManager { case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE"; case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT"; case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME"; - case FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED: return "AUTO_REVOKE_IF_UNUSED"; - case FLAG_PERMISSION_AUTO_REVOKE_USER_SET: return "AUTO_REVOKE_USER_SET"; case FLAG_PERMISSION_AUTO_REVOKED: return "AUTO_REVOKED"; default: return Integer.toString(flag); } diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java index ede264d042ce..578d53b17bf9 100644 --- a/core/java/android/database/ContentObserver.java +++ b/core/java/android/database/ContentObserver.java @@ -19,6 +19,9 @@ package android.database; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver.NotifyFlags; import android.net.Uri; @@ -26,12 +29,26 @@ import android.os.Handler; import android.os.UserHandle; import java.util.Arrays; +import java.util.Collection; /** * Receives call backs for changes to content. * Must be implemented by objects which are added to a {@link ContentObservable}. */ public abstract class ContentObserver { + /** + * Starting in {@link android.os.Build.VERSION_CODES#R}, there is a new + * public API overload {@link #onChange(boolean, Uri, int)} that delivers a + * {@code int flags} argument. + * <p> + * Some apps may be relying on a previous hidden API that delivered a + * {@code int userId} argument, and this change is used to control delivery + * of the new {@code int flags} argument in its place. + */ + @ChangeId + @EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q) + private static final long ADD_CONTENT_OBSERVER_FLAGS = 150939131L; + private final Object mLock = new Object(); private Transport mTransport; // guarded by mLock @@ -164,16 +181,26 @@ public abstract class ContentObserver { * @param uris The Uris of the changed content. * @param flags Flags indicating details about this change. */ - public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris, @NotifyFlags int flags) { + public void onChange(boolean selfChange, @NonNull Collection<Uri> uris, + @NotifyFlags int flags) { for (Uri uri : uris) { onChange(selfChange, uri, flags); } } /** @hide */ - public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris, @NotifyFlags int flags, - @UserIdInt int userId) { - onChange(selfChange, uris, flags); + public void onChange(boolean selfChange, @NonNull Collection<Uri> uris, + @NotifyFlags int flags, @UserIdInt int userId) { + // There are dozens of people relying on the hidden API inside the + // system UID, so hard-code the old behavior for all of them; for + // everyone else we gate based on a specific change + if (!CompatChanges.isChangeEnabled(ADD_CONTENT_OBSERVER_FLAGS) + || android.os.Process.myUid() == android.os.Process.SYSTEM_UID) { + // Deliver userId through argument to preserve hidden API behavior + onChange(selfChange, uris, userId); + } else { + onChange(selfChange, uris, flags); + } } /** @@ -186,7 +213,7 @@ public abstract class ContentObserver { * * @deprecated Callers should migrate towards using a richer overload that * provides more details about the change, such as - * {@link #dispatchChange(boolean, Iterable, int)}. + * {@link #dispatchChange(boolean, Collection, int)}. */ @Deprecated public final void dispatchChange(boolean selfChange) { @@ -206,7 +233,7 @@ public abstract class ContentObserver { * @param uri The Uri of the changed content. */ public final void dispatchChange(boolean selfChange, @Nullable Uri uri) { - dispatchChange(selfChange, Arrays.asList(uri), 0, UserHandle.getCallingUserId()); + dispatchChange(selfChange, uri, 0); } /** @@ -224,7 +251,7 @@ public abstract class ContentObserver { */ public final void dispatchChange(boolean selfChange, @Nullable Uri uri, @NotifyFlags int flags) { - dispatchChange(selfChange, Arrays.asList(uri), flags, UserHandle.getCallingUserId()); + dispatchChange(selfChange, Arrays.asList(uri), flags); } /** @@ -240,13 +267,13 @@ public abstract class ContentObserver { * @param uris The Uri of the changed content. * @param flags Flags indicating details about this change. */ - public final void dispatchChange(boolean selfChange, @NonNull Iterable<Uri> uris, + public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris, @NotifyFlags int flags) { dispatchChange(selfChange, uris, flags, UserHandle.getCallingUserId()); } /** @hide */ - public final void dispatchChange(boolean selfChange, @NonNull Iterable<Uri> uris, + public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris, @NotifyFlags int flags, @UserIdInt int userId) { if (mHandler == null) { onChange(selfChange, uris, flags, userId); diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java index 1855dd254ad4..ce86807ebf7a 100644 --- a/core/java/android/database/CursorToBulkCursorAdaptor.java +++ b/core/java/android/database/CursorToBulkCursorAdaptor.java @@ -20,10 +20,12 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.ContentResolver.NotifyFlags; import android.net.Uri; -import android.os.*; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; import java.util.ArrayList; - +import java.util.Collection; /** * Wraps a BulkCursor around an existing Cursor making it remotable. @@ -81,7 +83,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative } @Override - public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris, + public void onChange(boolean selfChange, @NonNull Collection<Uri> uris, @NotifyFlags int flags, @UserIdInt int userId) { // Since we deliver changes from the most-specific to least-specific // overloads, we only need to redirect from the most-specific local diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 85ef4a3392a8..a091f84fe463 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -846,6 +846,33 @@ public final class CameraManager { @NonNull String physicalCameraId) { // default empty implementation } + + /** + * A camera device has been opened by an application. + * + * <p>The default implementation of this method does nothing.</p> + * + * @param cameraId The unique identifier of the new camera. + * @param packageId The package Id of the application opening the camera. + * + * @see #onCameraClosed + */ + /** @hide */ + public void onCameraOpened(@NonNull String cameraId, @NonNull String packageId) { + // default empty implementation + } + + /** + * A previously-opened camera has been closed. + * + * <p>The default implementation of this method does nothing.</p> + * + * @param cameraId The unique identifier of the closed camera. + */ + /** @hide */ + public void onCameraClosed(@NonNull String cameraId) { + // default empty implementation + } } /** @@ -1276,6 +1303,12 @@ public final class CameraManager { } @Override public void onCameraAccessPrioritiesChanged() { + } + @Override + public void onCameraOpened(String id, String clientPackageId) { + } + @Override + public void onCameraClosed(String id) { }}; String[] cameraIds = null; @@ -1503,6 +1536,38 @@ public final class CameraManager { } } + private void postSingleCameraOpenedUpdate(final AvailabilityCallback callback, + final Executor executor, final String id, final String packageId) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute( + new Runnable() { + @Override + public void run() { + callback.onCameraOpened(id, packageId); + } + }); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void postSingleCameraClosedUpdate(final AvailabilityCallback callback, + final Executor executor, final String id) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute( + new Runnable() { + @Override + public void run() { + callback.onCameraClosed(id); + } + }); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final String physicalId, final int status) { if (isAvailable(status)) { @@ -1846,6 +1911,32 @@ public final class CameraManager { } } + @Override + public void onCameraOpened(String cameraId, String clientPackageId) { + synchronized (mLock) { + final int callbackCount = mCallbackMap.size(); + for (int i = 0; i < callbackCount; i++) { + Executor executor = mCallbackMap.valueAt(i); + final AvailabilityCallback callback = mCallbackMap.keyAt(i); + + postSingleCameraOpenedUpdate(callback, executor, cameraId, clientPackageId); + } + } + } + + @Override + public void onCameraClosed(String cameraId) { + synchronized (mLock) { + final int callbackCount = mCallbackMap.size(); + for (int i = 0; i < callbackCount; i++) { + Executor executor = mCallbackMap.valueAt(i); + final AvailabilityCallback callback = mCallbackMap.keyAt(i); + + postSingleCameraClosedUpdate(callback, executor, cameraId); + } + } + } + /** * Try to connect to camera service after some delay if any client registered camera * availability callback or torch status callback. diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 2a71da83027d..91451427b45e 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -129,10 +129,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing super(mac); } - public CryptoObject(@NonNull IdentityCredential credential) { - super(credential); - } - /** * Get {@link Signature} object. * @return {@link Signature} object or null if this doesn't contain one. @@ -160,8 +156,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing /** * Get {@link IdentityCredential} object. * @return {@link IdentityCredential} object or null if this doesn't contain one. + * @hide */ - public @Nullable IdentityCredential getIdentityCredential() { + public IdentityCredential getIdentityCredential() { return super.getIdentityCredential(); } } diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java index c5cb8037d4db..e90b57cdc7b4 100644 --- a/core/java/android/hardware/lights/Light.java +++ b/core/java/android/hardware/lights/Light.java @@ -37,7 +37,8 @@ public final class Light implements Parcelable { /** * Creates a new light with the given data. * - * @hide */ + * @hide + */ public Light(int id, int ordinal, int type) { mId = id; mOrdinal = ordinal; @@ -76,8 +77,24 @@ public final class Light implements Parcelable { } }; + @Override + public boolean equals(Object obj) { + if (obj instanceof Light) { + Light light = (Light) obj; + return mId == light.mId && mOrdinal == light.mOrdinal && mType == light.mType; + } + return false; + } + + @Override + public int hashCode() { + return mId; + } + /** * Returns the id of the light. + * + * <p>This is an opaque value used as a unique identifier for the light. */ public int getId() { return mId; @@ -86,11 +103,9 @@ public final class Light implements Parcelable { /** * Returns the ordinal of the light. * - * <p>This represents the physical order of the lights on the device. The exact values are - * device-dependent, but for example, if there are lights in a row, sorting the Light objects - * by ordinal should match the order in which they appear on the device. If the device has - * 4 lights, the ordinals could be [1, 2, 3, 4] or [0, 10, 20, 30] or any other values that - * have the same sort order. + * <p>This is a sort key that represents the physical order of lights on the device with the + * same type. In the case of multiple lights arranged in a line, for example, the ordinals + * could be [1, 2, 3, 4], or [0, 10, 20, 30], or any other values that have the same sort order. */ public int getOrdinal() { return mOrdinal; diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java index 1bc051b977a8..8cd231224472 100644 --- a/core/java/android/hardware/lights/LightsManager.java +++ b/core/java/android/hardware/lights/LightsManager.java @@ -161,7 +161,7 @@ public final class LightsManager { * @param request the settings for lights that should change */ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - public void setLights(@NonNull LightsRequest request) { + public void requestLights(@NonNull LightsRequest request) { Preconditions.checkNotNull(request); if (!mClosed) { try { diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java index a36da4c7d85d..5c4fc6707e96 100644 --- a/core/java/android/hardware/lights/LightsRequest.java +++ b/core/java/android/hardware/lights/LightsRequest.java @@ -86,7 +86,7 @@ public final class LightsRequest { * Create a LightsRequest object used to override lights on the device. * * <p>The generated {@link LightsRequest} should be used in - * {@link LightsManager.Session#setLights(LightsLightsRequest). + * {@link LightsManager.Session#requestLights(LightsLightsRequest). */ public @NonNull LightsRequest build() { return new LightsRequest(mChanges); diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java index bf641d72eab1..1aeb76a396b4 100644 --- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java +++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java @@ -28,7 +28,6 @@ import android.content.res.XmlResourceParser; import android.text.TextUtils; import android.util.ArraySet; import android.util.AttributeSet; -import android.util.Log; import android.util.Slog; import android.util.Xml; @@ -231,15 +230,7 @@ public class KeyphraseEnrollmentInfo { com.android.internal.R.styleable.VoiceEnrollmentApplication); keyphraseMetadata = getKeyphraseFromTypedArray(array, packageName, parseErrors); array.recycle(); - } catch (XmlPullParserException e) { - String error = "Error parsing keyphrase enrollment meta-data for " + packageName; - parseErrors.add(error + ": " + e); - Slog.w(TAG, error, e); - } catch (IOException e) { - String error = "Error parsing keyphrase enrollment meta-data for " + packageName; - parseErrors.add(error + ": " + e); - Slog.w(TAG, error, e); - } catch (PackageManager.NameNotFoundException e) { + } catch (XmlPullParserException | PackageManager.NameNotFoundException | IOException e) { String error = "Error parsing keyphrase enrollment meta-data for " + packageName; parseErrors.add(error + ": " + e); Slog.w(TAG, error, e); @@ -390,7 +381,6 @@ public class KeyphraseEnrollmentInfo { * False if not. */ public boolean isUidSupportedEnrollmentApplication(int uid) { - Log.d(TAG, "isUidSupportedEnrollmentApplication: " + toString()); return mEnrollmentApplicationUids.contains(uid); } diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index 0513feef801f..6efd03c44b9f 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -28,6 +28,7 @@ import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.View; import android.view.WindowManager; import java.lang.annotation.Retention; @@ -94,6 +95,13 @@ public class SoftInputWindow extends Dialog { lp.token = token; getWindow().setAttributes(lp); updateWindowState(SoftInputWindowState.TOKEN_SET); + + // As soon as we have a token, make sure the window is added (but not shown) by + // setting visibility to INVISIBLE and calling show() on Dialog. Note that + // WindowInsetsController.OnControllableInsetsChangedListener relies on the window + // being added to function. + getWindow().getDecorView().setVisibility(View.INVISIBLE); + show(); return; case SoftInputWindowState.TOKEN_SET: case SoftInputWindowState.SHOWN_AT_LEAST_ONCE: diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java index 139f5bebcd89..83b5f63576f2 100644 --- a/core/java/android/net/EthernetManager.java +++ b/core/java/android/net/EthernetManager.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -248,6 +249,10 @@ public class EthernetManager { * interface, the existing interface will be used. * @param callback A callback to be called once the request has been fulfilled. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_STACK, + android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK + }) @NonNull public TetheredInterfaceRequest requestTetheredInterface(@NonNull final Executor executor, @NonNull final TetheredInterfaceCallback callback) { diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index fe7c7c921b67..c889ee6307d1 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -248,6 +248,27 @@ public class Binder implements IBinder { } } + static ThreadLocal<Boolean> sWarnOnBlockingOnCurrentThread = + ThreadLocal.withInitial(() -> sWarnOnBlocking); + + /** + * Allow blocking calls for the current thread. See {@link #allowBlocking}. + * + * @hide + */ + public static void allowBlockingForCurrentThread() { + sWarnOnBlockingOnCurrentThread.set(false); + } + + /** + * Reset the current thread to the default blocking behavior. See {@link #defaultBlocking}. + * + * @hide + */ + public static void defaultBlockingForCurrentThread() { + sWarnOnBlockingOnCurrentThread.set(sWarnOnBlocking); + } + /** * Raw native pointer to JavaBBinderHolder object. Owned by this Java object. Not null. */ diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index be307ab4737d..20e5f243163f 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -479,16 +479,21 @@ public final class BinderProxy implements IBinder { public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { Binder.checkParcel(this, code, data, "Unreasonably large binder buffer"); - if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) { + if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0) + && Binder.sWarnOnBlockingOnCurrentThread.get()) { + // For now, avoid spamming the log by disabling after we've logged // about this interface at least once mWarnOnBlocking = false; + if (Build.IS_USERDEBUG) { // Log this as a WTF on userdebug builds. - Log.wtf(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY", + Log.wtf(Binder.TAG, + "Outgoing transactions from this process must be FLAG_ONEWAY", new Throwable()); } else { - Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY", + Log.w(Binder.TAG, + "Outgoing transactions from this process must be FLAG_ONEWAY", new Throwable()); } } @@ -521,7 +526,7 @@ public final class BinderProxy implements IBinder { final AppOpsManager.PausedNotedAppOpsCollection prevCollection = AppOpsManager.pauseNotedAppOpsCollection(); - if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isCollectingNotedAppOps()) { + if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) { flags |= FLAG_COLLECT_NOTED_APP_OPS; } diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 21a1e0f0a108..f2fb5b246f39 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.Objects; /** * Provides access to environment variables. @@ -1253,6 +1254,50 @@ public class Environment { uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED; } + /** + * Returns whether the calling app has All Files Access on the primary shared/external storage + * media. + * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't + * enough to gain the access. + * <p>To request access, use + * {@link android.provider.Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION}. + */ + public static boolean isExternalStorageManager() { + final File externalDir = sCurrentUser.getExternalDirs()[0]; + return isExternalStorageManager(externalDir); + } + + /** + * Returns whether the calling app has All Files Access at the given {@code path} + * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't + * enough to gain the access. + * <p>To request access, use + * {@link android.provider.Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION}. + */ + public static boolean isExternalStorageManager(@NonNull File path) { + final Context context = Objects.requireNonNull(AppGlobals.getInitialApplication()); + String packageName = Objects.requireNonNull(context.getPackageName()); + int uid = context.getApplicationInfo().uid; + + final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); + final int opMode = + appOps.checkOpNoThrow(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE, uid, packageName); + + switch (opMode) { + case AppOpsManager.MODE_DEFAULT: + return PackageManager.PERMISSION_GRANTED + == context.checkPermission( + Manifest.permission.MANAGE_EXTERNAL_STORAGE, Process.myPid(), uid); + case AppOpsManager.MODE_ALLOWED: + return true; + case AppOpsManager.MODE_ERRORED: + case AppOpsManager.MODE_IGNORED: + return false; + default: + throw new IllegalStateException("Unknown AppOpsManager mode " + opMode); + } + } + static File getDirectory(String variableName, String defaultPath) { String path = System.getenv(variableName); return path == null ? new File(defaultPath) : new File(path); diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 47f24615d60a..a10a456bd6a6 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -22,6 +22,7 @@ import static com.android.internal.util.Preconditions.checkCollectionNotEmpty; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentInterface; import android.content.ContentProvider; @@ -1303,6 +1304,7 @@ public final class DocumentsContract { * {@hide} */ @SystemApi + @TestApi public static @NonNull Uri setManageMode(@NonNull Uri uri) { Preconditions.checkNotNull(uri, "uri can not be null"); return uri.buildUpon().appendQueryParameter(PARAM_MANAGE, "true").build(); @@ -1314,6 +1316,7 @@ public final class DocumentsContract { * {@hide} */ @SystemApi + @TestApi public static boolean isManageMode(@NonNull Uri uri) { Preconditions.checkNotNull(uri, "uri can not be null"); return uri.getBooleanQueryParameter(PARAM_MANAGE, false); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 641de4a71142..23c074c88f01 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -98,7 +98,8 @@ import java.util.Set; * The Settings provider contains global system-level device preferences. */ public final class Settings { - private static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false; + /** @hide */ + public static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false; // Intent actions for Settings @@ -709,10 +710,7 @@ public final class Settings { * If not specified, the default behavior is * {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_WEAK}. * <p> - * Output: Returns {@link android.app.Activity#RESULT_CANCELED} if the user already has an - * authenticator that meets the requirements, or if the device cannot fulfill the request - * (e.g. does not have biometric hardware). Returns {@link android.app.Activity#RESULT_OK} - * otherwise. Note that callers should still check + * Output: Nothing. Note that callers should still check * {@link android.hardware.biometrics.BiometricManager#canAuthenticate(int)} * afterwards to ensure that the user actually completed enrollment. */ diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 97f7533c3209..03b38ab9b3ee 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; @@ -3734,7 +3735,6 @@ public final class Telephony { * @deprecated this column is no longer supported, use {@link #NETWORK_TYPE_BITMASK} instead */ @Deprecated - @SystemApi public static final String BEARER_BITMASK = "bearer_bitmask"; /** @@ -3784,7 +3784,6 @@ public final class Telephony { * <p>Type: INTEGER</p> *@hide */ - @SystemApi public static final String PROFILE_ID = "profile_id"; /** @@ -3985,7 +3984,6 @@ public final class Telephony { * * @hide */ - @SystemApi public static final String SKIP_464XLAT = "skip_464xlat"; /** @@ -3994,7 +3992,6 @@ public final class Telephony { * * @hide */ - @SystemApi public static final int SKIP_464XLAT_DEFAULT = -1; /** @@ -4003,7 +4000,6 @@ public final class Telephony { * * @hide */ - @SystemApi public static final int SKIP_464XLAT_DISABLE = 0; /** @@ -4012,7 +4008,6 @@ public final class Telephony { * * @hide */ - @SystemApi public static final int SKIP_464XLAT_ENABLE = 1; @@ -4036,6 +4031,7 @@ public final class Telephony { * @hide */ @ChangeId + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q) public static final long APN_READING_PERMISSION_CHANGE_ID = 124107808L; } @@ -4390,6 +4386,7 @@ public final class Telephony { * Indicates that whether the message has been broadcasted to the application. * <P>Type: BOOLEAN</P> */ + // TODO: deprecate this in S. public static final String MESSAGE_BROADCASTED = "message_broadcasted"; /** diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 72e9ad047ed7..8f858d547b1f 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -77,6 +77,15 @@ public final class FillRequest implements Parcelable { */ public static final @RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2; + /** + * Indicates the request came from a password field. + * + * (TODO: b/141703197) Temporary fix for augmented autofill showing passwords. + * + * @hide + */ + public static final @RequestFlags int FLAG_PASSWORD_INPUT_TYPE = 0x4; + /** @hide */ public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE; @@ -149,7 +158,8 @@ public final class FillRequest implements Parcelable { /** @hide */ @IntDef(flag = true, prefix = "FLAG_", value = { FLAG_MANUAL_REQUEST, - FLAG_COMPATIBILITY_MODE_REQUEST + FLAG_COMPATIBILITY_MODE_REQUEST, + FLAG_PASSWORD_INPUT_TYPE }) @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -169,6 +179,8 @@ public final class FillRequest implements Parcelable { return "FLAG_MANUAL_REQUEST"; case FLAG_COMPATIBILITY_MODE_REQUEST: return "FLAG_COMPATIBILITY_MODE_REQUEST"; + case FLAG_PASSWORD_INPUT_TYPE: + return "FLAG_PASSWORD_INPUT_TYPE"; default: return Integer.toHexString(value); } } @@ -223,7 +235,8 @@ public final class FillRequest implements Parcelable { Preconditions.checkFlagsArgument( mFlags, FLAG_MANUAL_REQUEST - | FLAG_COMPATIBILITY_MODE_REQUEST); + | FLAG_COMPATIBILITY_MODE_REQUEST + | FLAG_PASSWORD_INPUT_TYPE); this.mInlineSuggestionsRequest = inlineSuggestionsRequest; onConstructed(); @@ -352,7 +365,8 @@ public final class FillRequest implements Parcelable { Preconditions.checkFlagsArgument( mFlags, FLAG_MANUAL_REQUEST - | FLAG_COMPATIBILITY_MODE_REQUEST); + | FLAG_COMPATIBILITY_MODE_REQUEST + | FLAG_PASSWORD_INPUT_TYPE); this.mInlineSuggestionsRequest = inlineSuggestionsRequest; onConstructed(); @@ -373,10 +387,10 @@ public final class FillRequest implements Parcelable { }; @DataClass.Generated( - time = 1575928271155L, + time = 1583196707026L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", - inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") + inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index e8e1223a29d0..06b5fa07a554 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -89,7 +89,7 @@ public final class FillResponse implements Parcelable { private final @Nullable int[] mCancelIds; private final boolean mSupportsInlineSuggestions; // TODO(b/149240554): revert back to use ParceledListSlice after the bug is resolved. - private final @Nullable ArrayList<InlinePresentation> mInlineActions; + private final @Nullable ArrayList<InlineAction> mInlineActions; private FillResponse(@NonNull Builder builder) { mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null; @@ -213,7 +213,7 @@ public final class FillResponse implements Parcelable { } /** @hide */ - public @Nullable List<InlinePresentation> getInlineActions() { + public @Nullable List<InlineAction> getInlineActions() { return mInlineActions; } @@ -239,7 +239,7 @@ public final class FillResponse implements Parcelable { private UserData mUserData; private int[] mCancelIds; private boolean mSupportsInlineSuggestions; - private ArrayList<InlinePresentation> mInlineActions; + private ArrayList<InlineAction> mInlineActions; /** * Triggers a custom UI before before autofilling the screen with any data set in this @@ -656,15 +656,12 @@ public final class FillResponse implements Parcelable { } /** - * Adds a new {@link InlinePresentation} to this response representing an action UI. - * - * <p> For example, the UI can be associated with an intent which can open an activity for - * the user to manage the Autofill provider settings. + * Adds a new {@link InlineAction} to this response representing an action UI. * * @return This builder. */ @NonNull - public Builder addInlineAction(@NonNull InlinePresentation inlineAction) { + public Builder addInlineAction(@NonNull InlineAction inlineAction) { throwIfDestroyed(); throwIfAuthenticationCalled(); if (mInlineActions == null) { @@ -881,10 +878,10 @@ public final class FillResponse implements Parcelable { final int[] cancelIds = parcel.createIntArray(); builder.setPresentationCancelIds(cancelIds); - final List<InlinePresentation> inlineActions = parcel.createTypedArrayList( - InlinePresentation.CREATOR); + final List<InlineAction> inlineActions = parcel.createTypedArrayList( + InlineAction.CREATOR); if (inlineActions != null) { - for (InlinePresentation inlineAction : inlineActions) { + for (InlineAction inlineAction : inlineActions) { builder.addInlineAction(inlineAction); } } diff --git a/core/java/android/service/autofill/InlineAction.aidl b/core/java/android/service/autofill/InlineAction.aidl new file mode 100644 index 000000000000..7f8511825d73 --- /dev/null +++ b/core/java/android/service/autofill/InlineAction.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.autofill; + +parcelable InlineAction; diff --git a/core/java/android/service/autofill/InlineAction.java b/core/java/android/service/autofill/InlineAction.java new file mode 100644 index 000000000000..17c4b33ba96b --- /dev/null +++ b/core/java/android/service/autofill/InlineAction.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.autofill; + +import android.annotation.NonNull; +import android.content.IntentSender; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * Represents an inline action as part of the autofill/augmented autofill response. + * + * <p> It includes both the action intent and the UI presentation. For example, the UI can be + * associated with an intent which can open an activity for the user to manage the Autofill provider + * settings. + */ +@DataClass( + genToString = true, + genHiddenConstDefs = true, + genEqualsHashCode = true) +public final class InlineAction implements Parcelable { + + /** + * Representation of the inline action. + */ + private final @NonNull InlinePresentation mInlinePresentation; + + /** + * The associated intent which will be triggered when the action is selected. It will only be + * called by the OS. + */ + private final @NonNull IntentSender mAction; + + + + // Code below generated by codegen v1.0.15. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/InlineAction.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new InlineAction. + * + * @param inlinePresentation + * Representation of the inline action. + * @param action + * The associated intent which will be triggered when the action is selected. It will only be + * invoked by the OS. + */ + @DataClass.Generated.Member + public InlineAction( + @NonNull InlinePresentation inlinePresentation, + @NonNull IntentSender action) { + this.mInlinePresentation = inlinePresentation; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mInlinePresentation); + this.mAction = action; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAction); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * Representation of the inline action. + */ + @DataClass.Generated.Member + public @NonNull InlinePresentation getInlinePresentation() { + return mInlinePresentation; + } + + /** + * The associated intent which will be triggered when the action is selected. It will only be + * invoked by the OS. + */ + @DataClass.Generated.Member + public @NonNull IntentSender getAction() { + return mAction; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "InlineAction { " + + "inlinePresentation = " + mInlinePresentation + ", " + + "action = " + mAction + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(InlineAction other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + InlineAction that = (InlineAction) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mInlinePresentation, that.mInlinePresentation) + && java.util.Objects.equals(mAction, that.mAction); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentation); + _hash = 31 * _hash + java.util.Objects.hashCode(mAction); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeTypedObject(mInlinePresentation, flags); + dest.writeTypedObject(mAction, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ InlineAction(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + InlinePresentation inlinePresentation = (InlinePresentation) in.readTypedObject(InlinePresentation.CREATOR); + IntentSender action = (IntentSender) in.readTypedObject(IntentSender.CREATOR); + + this.mInlinePresentation = inlinePresentation; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mInlinePresentation); + this.mAction = action; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAction); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<InlineAction> CREATOR + = new Parcelable.Creator<InlineAction>() { + @Override + public InlineAction[] newArray(int size) { + return new InlineAction[size]; + } + + @Override + public InlineAction createFromParcel(@NonNull android.os.Parcel in) { + return new InlineAction(in); + } + }; + + @DataClass.Generated( + time = 1583798182424L, + codegenVersion = "1.0.15", + sourceFile = "frameworks/base/core/java/android/service/autofill/InlineAction.java", + inputSignatures = "private final @android.annotation.NonNull android.service.autofill.InlinePresentation mInlinePresentation\nprivate final @android.annotation.NonNull android.content.IntentSender mAction\nclass InlineAction extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index fe792b1efd9f..ed27dd568823 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -40,7 +40,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.service.autofill.Dataset; import android.service.autofill.FillEventHistory; -import android.service.autofill.InlinePresentation; +import android.service.autofill.InlineAction; import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams; import android.util.Log; import android.util.Pair; @@ -559,7 +559,7 @@ public abstract class AugmentedAutofillService extends Service { } void reportResult(@Nullable List<Dataset> inlineSuggestionsData, - @Nullable List<InlinePresentation> inlineActions) { + @Nullable List<InlineAction> inlineActions) { try { mCallback.onSuccess(inlineSuggestionsData, inlineActions); } catch (RemoteException e) { diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java index b7fdf5ab337f..f564b3ba616a 100644 --- a/core/java/android/service/autofill/augmented/FillResponse.java +++ b/core/java/android/service/autofill/augmented/FillResponse.java @@ -21,7 +21,7 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.Bundle; import android.service.autofill.Dataset; -import android.service.autofill.InlinePresentation; +import android.service.autofill.InlineAction; import com.android.internal.util.DataClass; @@ -53,11 +53,10 @@ public final class FillResponse { private @Nullable List<Dataset> mInlineSuggestions; /** - * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no - * inline actions are provided. + * Defaults to null if no inline actions are provided. */ @DataClass.PluralOf("inlineAction") - private @Nullable List<InlinePresentation> mInlineActions; + private @Nullable List<InlineAction> mInlineActions; /** * The client state that {@link AugmentedAutofillService} implementation can put anything in to @@ -74,7 +73,7 @@ public final class FillResponse { return null; } - private static List<InlinePresentation> defaultInlineActions() { + private static List<InlineAction> defaultInlineActions() { return null; } @@ -86,12 +85,12 @@ public final class FillResponse { /** @hide */ abstract static class BaseBuilder { abstract FillResponse.Builder addInlineSuggestion(@NonNull Dataset value); - abstract FillResponse.Builder addInlineAction(@NonNull InlinePresentation value); + abstract FillResponse.Builder addInlineAction(@NonNull InlineAction value); } - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -108,7 +107,7 @@ public final class FillResponse { /* package-private */ FillResponse( @Nullable FillWindow fillWindow, @Nullable List<Dataset> inlineSuggestions, - @Nullable List<InlinePresentation> inlineActions, + @Nullable List<InlineAction> inlineActions, @Nullable Bundle clientState) { this.mFillWindow = fillWindow; this.mInlineSuggestions = inlineSuggestions; @@ -140,13 +139,12 @@ public final class FillResponse { } /** - * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no - * inline actions are provided. + * Defaults to null if no inline actions are provided. * * @hide */ @DataClass.Generated.Member - public @Nullable List<InlinePresentation> getInlineActions() { + public @Nullable List<InlineAction> getInlineActions() { return mInlineActions; } @@ -171,7 +169,7 @@ public final class FillResponse { private @Nullable FillWindow mFillWindow; private @Nullable List<Dataset> mInlineSuggestions; - private @Nullable List<InlinePresentation> mInlineActions; + private @Nullable List<InlineAction> mInlineActions; private @Nullable Bundle mClientState; private long mBuilderFieldsSet = 0L; @@ -183,7 +181,7 @@ public final class FillResponse { * The {@link FillWindow} used to display the Autofill UI. */ @DataClass.Generated.Member - public @NonNull Builder setFillWindow(@Nullable FillWindow value) { + public @NonNull Builder setFillWindow(@NonNull FillWindow value) { checkNotUsed(); mBuilderFieldsSet |= 0x1; mFillWindow = value; @@ -195,7 +193,7 @@ public final class FillResponse { * inline suggestions are available from the service. */ @DataClass.Generated.Member - public @NonNull Builder setInlineSuggestions(@Nullable List<Dataset> value) { + public @NonNull Builder setInlineSuggestions(@NonNull List<Dataset> value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; mInlineSuggestions = value; @@ -212,11 +210,10 @@ public final class FillResponse { } /** - * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no - * inline actions are provided. + * Defaults to null if no inline actions are provided. */ @DataClass.Generated.Member - public @NonNull Builder setInlineActions(@Nullable List<InlinePresentation> value) { + public @NonNull Builder setInlineActions(@NonNull List<InlineAction> value) { checkNotUsed(); mBuilderFieldsSet |= 0x4; mInlineActions = value; @@ -226,7 +223,7 @@ public final class FillResponse { /** @see #setInlineActions */ @DataClass.Generated.Member @Override - @NonNull FillResponse.Builder addInlineAction(@NonNull InlinePresentation value) { + @NonNull FillResponse.Builder addInlineAction(@NonNull InlineAction value) { if (mInlineActions == null) setInlineActions(new ArrayList<>()); mInlineActions.add(value); return this; @@ -238,7 +235,7 @@ public final class FillResponse { * {@link AugmentedAutofillService#getFillEventHistory()}. */ @DataClass.Generated.Member - public @NonNull Builder setClientState(@Nullable Bundle value) { + public @NonNull Builder setClientState(@NonNull Bundle value) { checkNotUsed(); mBuilderFieldsSet |= 0x8; mClientState = value; @@ -279,10 +276,10 @@ public final class FillResponse { } @DataClass.Generated( - time = 1582682935951L, - codegenVersion = "1.0.14", + time = 1583793032373L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillResponse.java", - inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineAction\") @android.annotation.Nullable java.util.List<android.service.autofill.InlinePresentation> mInlineActions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static java.util.List<android.service.autofill.InlinePresentation> defaultInlineActions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineAction(android.service.autofill.InlinePresentation)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineAction\") @android.annotation.Nullable java.util.List<android.service.autofill.InlineAction> mInlineActions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static java.util.List<android.service.autofill.InlineAction> defaultInlineActions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineAction(android.service.autofill.InlineAction)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl index bf0adcd54ab1..545dab284067 100644 --- a/core/java/android/service/autofill/augmented/IFillCallback.aidl +++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl @@ -20,7 +20,7 @@ import android.os.Bundle; import android.os.ICancellationSignal; import android.service.autofill.Dataset; -import android.service.autofill.InlinePresentation; +import android.service.autofill.InlineAction; import java.util.List; @@ -32,7 +32,7 @@ import java.util.List; interface IFillCallback { void onCancellable(in ICancellationSignal cancellation); void onSuccess(in @nullable List<Dataset> inlineSuggestionsData, - in @nullable List<InlinePresentation> inlineActions); + in @nullable List<InlineAction> inlineActions); boolean isCompleted(); void cancel(); } diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java index a481ec8382ed..8cd9eea1e6e0 100644 --- a/core/java/android/service/contentcapture/DataShareReadAdapter.java +++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.ParcelFileDescriptor; +import android.view.contentcapture.ContentCaptureManager.DataShareError; /** * Adapter class to be used for the Content Capture Service app to propagate the status of the @@ -46,5 +47,5 @@ public interface DataShareReadAdapter { * these 2 events is not defined, and it's important that the service treats end of stream * correctly in this situation. **/ - void onError(int errorCode); + void onError(@DataShareError int errorCode); } diff --git a/core/java/android/service/controls/TokenProvider.aidl b/core/java/android/service/controls/TokenProvider.aidl deleted file mode 100644 index 8f4b7953f659..000000000000 --- a/core/java/android/service/controls/TokenProvider.aidl +++ /dev/null @@ -1,7 +0,0 @@ -package android.service.controls; - -/** @hide */ -interface TokenProvider { - void setAuthToken(String token); - String getAccountName(); -}
\ No newline at end of file diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 002d4b8d195d..e70311fb9429 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -609,9 +609,7 @@ public class DreamService extends Service implements Window.Callback { * * @hide * - * TODO: Remove @UnsupportedAppUsage. */ - @UnsupportedAppUsage public void setWindowless(boolean windowless) { mWindowless = windowless; } diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 74b913645ad4..1beeb6554ee1 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -237,16 +237,24 @@ public class StatusBarNotification implements Parcelable { public StatusBarNotification cloneLight() { final Notification no = new Notification(); this.notification.cloneInto(no, false); // light copy - return new StatusBarNotification(this.pkg, this.opPkg, - this.id, this.tag, this.uid, this.initialPid, - no, this.user, this.overrideGroupKey, this.postTime); + return cloneShallow(no); } @Override public StatusBarNotification clone() { - return new StatusBarNotification(this.pkg, this.opPkg, + return cloneShallow(this.notification.clone()); + } + + /** + * @param notification Some kind of clone of this.notification. + * @return A shallow copy of self, with notification in place of this.notification. + */ + StatusBarNotification cloneShallow(Notification notification) { + StatusBarNotification result = new StatusBarNotification(this.pkg, this.opPkg, this.id, this.tag, this.uid, this.initialPid, - this.notification.clone(), this.user, this.overrideGroupKey, this.postTime); + notification, this.user, this.overrideGroupKey, this.postTime); + result.setInstanceId(this.mInstanceId); + return result; } @Override diff --git a/core/java/android/view/ITaskOrganizer.aidl b/core/java/android/view/ITaskOrganizer.aidl index 5ccdd30a9d97..565f694b8a55 100644 --- a/core/java/android/view/ITaskOrganizer.aidl +++ b/core/java/android/view/ITaskOrganizer.aidl @@ -27,7 +27,7 @@ import android.app.ActivityManager; */ oneway interface ITaskOrganizer { void taskAppeared(in ActivityManager.RunningTaskInfo taskInfo); - void taskVanished(in IWindowContainer container); + void taskVanished(in ActivityManager.RunningTaskInfo taskInfo); /** * Called upon completion of diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 73601d968040..84ac90bae258 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -724,11 +724,12 @@ interface IWindowManager /** * Called to get the expected window insets. - * TODO(window-context): Remove when new insets flag is available. + * + * @return {@code true} if system bars are always comsumed. */ - void getWindowInsets(in WindowManager.LayoutParams attrs, int displayId, + boolean getWindowInsets(in WindowManager.LayoutParams attrs, int displayId, out Rect outContentInsets, out Rect outStableInsets, - out DisplayCutout.ParcelableWrapper displayCutout); + out DisplayCutout.ParcelableWrapper outDisplayCutout, out InsetsState outInsetsState); /** * Called to show global actions. diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 573d8fc65c84..607886f3b13b 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -52,6 +52,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -59,6 +60,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.function.BiFunction; /** @@ -306,6 +308,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private SyncRtSurfaceTransactionApplier mApplier; private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; + private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners + = new ArrayList<>(); + + /** Set of inset types for which an animation was started since last resetting this field */ + private @InsetsType int mLastStartedAnimTypes; public InsetsController(ViewRootImpl viewRoot) { this(viewRoot, (controller, type) -> { @@ -459,6 +466,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } mTmpControlArray.clear(); + + // Do not override any animations that the app started in the OnControllableInsetsChanged + // listeners. + int animatingTypes = invokeControllableInsetsChangedListeners(); + showTypes[0] &= ~animatingTypes; + hideTypes[0] &= ~animatingTypes; + if (showTypes[0] != 0) { applyAnimation(showTypes[0], true /* show */, false /* fromIme */); } @@ -542,9 +556,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs, @Nullable Interpolator interpolator, @AnimationType int animationType) { - // If the frame of our window doesn't span the entire display, the control API makes very - // little sense, as we don't deal with negative insets. So just cancel immediately. - if (!mState.getDisplayFrame().equals(mFrame)) { + if (!checkDisplayFramesForControlling()) { listener.onCancelled(); CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.cancel(); @@ -554,6 +566,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types)); } + private boolean checkDisplayFramesForControlling() { + + // If the frame of our window doesn't span the entire display, the control API makes very + // little sense, as we don't deal with negative insets. So just cancel immediately. + return mState.getDisplayFrame().equals(mFrame); + } + private CancellationSignal controlAnimationUnchecked(@InsetsType int types, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, long durationMs, Interpolator interpolator, boolean fade, @@ -567,6 +586,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return cancellationSignal; } cancelExistingControllers(types); + mLastStartedAnimTypes |= types; final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); @@ -724,12 +744,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView); } if (mViewRoot.mView.isHardwareAccelerated()) { - mApplier.scheduleApply(params); + mApplier.scheduleApply(false /* earlyWakeup */, params); } else { // Window doesn't support hardware acceleration, no synchronization for now. // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every // frame instead. - mApplier.applyParams(new Transaction(), -1 /* frame */, params); + mApplier.applyParams(new Transaction(), -1 /* frame */, false /* earlyWakeup */, + params); } } @@ -992,4 +1013,65 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } return mViewRoot.mWindowAttributes.insetsFlags.behavior; } + + private @InsetsType int calculateControllableTypes() { + if (!checkDisplayFramesForControlling()) { + return 0; + } + @InsetsType int result = 0; + for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { + InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + if (consumer.getControl() != null) { + result |= toPublicType(consumer.mType); + } + } + return result; + } + + /** + * @return The types that are now animating due to a listener invoking control/show/hide + */ + private @InsetsType int invokeControllableInsetsChangedListeners() { + mLastStartedAnimTypes = 0; + @InsetsType int types = calculateControllableTypes(); + int size = mControllableInsetsChangedListeners.size(); + for (int i = 0; i < size; i++) { + mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types); + } + return mLastStartedAnimTypes; + } + + @Override + public void addOnControllableInsetsChangedListener( + OnControllableInsetsChangedListener listener) { + Objects.requireNonNull(listener); + mControllableInsetsChangedListeners.add(listener); + listener.onControllableInsetsChanged(this, calculateControllableTypes()); + } + + @Override + public void removeOnControllableInsetsChangedListener( + OnControllableInsetsChangedListener listener) { + Objects.requireNonNull(listener); + mControllableInsetsChangedListeners.remove(listener); + } + + /** + * At the time we receive new leashes (e.g. InsetsSourceConsumer is processing + * setControl) we need to release the old leash. But we may have already scheduled + * a SyncRtSurfaceTransaction applier to use it from the RenderThread. To avoid + * synchronization issues we also release from the RenderThread so this release + * happens after any existing items on the work queue. + */ + public void releaseSurfaceControlFromRt(SurfaceControl sc) { + if (mViewRoot.mView != null && mViewRoot.mView.isHardwareAccelerated()) { + mViewRoot.registerRtFrameCallback(frame -> { + sc.release(); + }); + // Make sure a frame gets scheduled. + mViewRoot.mView.invalidate(); + } else { + sc.release(); + } + } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index f07f1ce186fe..252fc0c82cf7 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -117,7 +117,7 @@ public class InsetsSourceConsumer { } } if (lastControl != null) { - lastControl.release(); + lastControl.release(mController); } } diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 29ba56a5fbb9..75f6eab798ce 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -94,9 +94,9 @@ public class InsetsSourceControl implements Parcelable { dest.writeParcelable(mSurfacePosition, 0 /* flags*/); } - public void release() { + public void release(InsetsController controller) { if (mLeash != null) { - mLeash.release(); + controller.releaseSurfaceControlFromRt(mLeash); } } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index e4aa41074221..c2ad74a566e9 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -80,6 +80,12 @@ public class InsetsState implements Parcelable { }) public @interface InternalInsetsType {} + /** + * Special value to be used to by methods returning an {@link InternalInsetsType} to indicate + * that the objects/parameters aren't associated with an {@link InternalInsetsType} + */ + public static final int ITYPE_INVALID = -1; + static final int FIRST_TYPE = 0; public static final int ITYPE_STATUS_BAR = FIRST_TYPE; diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java index c0ed9359c613..7f3641803770 100644 --- a/core/java/android/view/PendingInsetsController.java +++ b/core/java/android/view/PendingInsetsController.java @@ -38,6 +38,8 @@ public class PendingInsetsController implements WindowInsetsController { private @Behavior int mBehavior = KEEP_BEHAVIOR; private final InsetsState mDummyState = new InsetsState(); private InsetsController mReplayedInsetsController; + private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners + = new ArrayList<>(); @Override public void show(int types) { @@ -112,6 +114,27 @@ public class PendingInsetsController implements WindowInsetsController { return mDummyState; } + @Override + public void addOnControllableInsetsChangedListener( + OnControllableInsetsChangedListener listener) { + if (mReplayedInsetsController != null) { + mReplayedInsetsController.addOnControllableInsetsChangedListener(listener); + } else { + mControllableInsetsChangedListeners.add(listener); + listener.onControllableInsetsChanged(this, 0); + } + } + + @Override + public void removeOnControllableInsetsChangedListener( + OnControllableInsetsChangedListener listener) { + if (mReplayedInsetsController != null) { + mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener); + } else { + mControllableInsetsChangedListeners.remove(listener); + } + } + /** * Replays the commands on {@code controller} and attaches it to this instance such that any * calls will be forwarded to the real instance in the future. @@ -128,9 +151,15 @@ public class PendingInsetsController implements WindowInsetsController { for (int i = 0; i < size; i++) { mRequests.get(i).replay(controller); } + size = mControllableInsetsChangedListeners.size(); + for (int i = 0; i < size; i++) { + controller.addOnControllableInsetsChangedListener( + mControllableInsetsChangedListeners.get(i)); + } // Reset all state so it doesn't get applied twice just in case mRequests.clear(); + mControllableInsetsChangedListeners.clear(); mBehavior = KEEP_BEHAVIOR; mAppearance = 0; mAppearanceMask = 0; diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index b04372a1ce19..3c22ed80cb86 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -20,9 +20,11 @@ import static android.view.RemoteAnimationTargetProto.CLIP_RECT; import static android.view.RemoteAnimationTargetProto.CONTENT_INSETS; import static android.view.RemoteAnimationTargetProto.IS_TRANSLUCENT; import static android.view.RemoteAnimationTargetProto.LEASH; +import static android.view.RemoteAnimationTargetProto.LOCAL_BOUNDS; import static android.view.RemoteAnimationTargetProto.MODE; import static android.view.RemoteAnimationTargetProto.POSITION; import static android.view.RemoteAnimationTargetProto.PREFIX_ORDER_INDEX; +import static android.view.RemoteAnimationTargetProto.SCREEN_SPACE_BOUNDS; import static android.view.RemoteAnimationTargetProto.SOURCE_CONTAINER_BOUNDS; import static android.view.RemoteAnimationTargetProto.START_BOUNDS; import static android.view.RemoteAnimationTargetProto.START_LEASH; @@ -130,19 +132,38 @@ public class RemoteAnimationTarget implements Parcelable { * The source position of the app, in screen spaces coordinates. If the position of the leash * is modified from the controlling app, any animation transform needs to be offset by this * amount. + * @deprecated Use {@link #localBounds} instead. */ + @Deprecated @UnsupportedAppUsage public final Point position; /** + * Bounds of the target relative to its parent. + * When the app target animating on its parent, we need to use the local coordinates relative to + * its parent with {@code localBounds.left} & {@code localBounds.top} rather than using + * {@code position} in screen coordinates. + */ + public final Rect localBounds; + + /** * The bounds of the source container the app lives in, in screen space coordinates. If the crop * of the leash is modified from the controlling app, it needs to take the source container * bounds into account when calculating the crop. + * @deprecated Renamed to {@link #screenSpaceBounds} */ + @Deprecated @UnsupportedAppUsage public final Rect sourceContainerBounds; /** + * Bounds of the target relative to the screen. If the crop of the leash is modified from the + * controlling app, it needs to take the screen space bounds into account when calculating the + * crop. + */ + public final Rect screenSpaceBounds; + + /** * The starting bounds of the source container in screen space coordinates. This is {@code null} * if the animation target isn't MODE_CHANGING. Since this is the starting bounds, it's size * should be equivalent to the size of the starting thumbnail. Note that sourceContainerBounds @@ -165,7 +186,8 @@ public class RemoteAnimationTarget implements Parcelable { public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, - Rect sourceContainerBounds, WindowConfiguration windowConfig, boolean isNotInRecents, + Rect localBounds, Rect screenSpaceBounds, + WindowConfiguration windowConfig, boolean isNotInRecents, SurfaceControl startLeash, Rect startBounds) { this.mode = mode; this.taskId = taskId; @@ -175,7 +197,9 @@ public class RemoteAnimationTarget implements Parcelable { this.contentInsets = new Rect(contentInsets); this.prefixOrderIndex = prefixOrderIndex; this.position = new Point(position); - this.sourceContainerBounds = new Rect(sourceContainerBounds); + this.localBounds = new Rect(localBounds); + this.sourceContainerBounds = new Rect(screenSpaceBounds); + this.screenSpaceBounds = new Rect(screenSpaceBounds); this.windowConfiguration = windowConfig; this.isNotInRecents = isNotInRecents; this.startLeash = startLeash; @@ -191,7 +215,9 @@ public class RemoteAnimationTarget implements Parcelable { contentInsets = in.readParcelable(null); prefixOrderIndex = in.readInt(); position = in.readParcelable(null); + localBounds = in.readParcelable(null); sourceContainerBounds = in.readParcelable(null); + screenSpaceBounds = in.readParcelable(null); windowConfiguration = in.readParcelable(null); isNotInRecents = in.readBoolean(); startLeash = in.readParcelable(null); @@ -213,7 +239,9 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeParcelable(contentInsets, 0 /* flags */); dest.writeInt(prefixOrderIndex); dest.writeParcelable(position, 0 /* flags */); + dest.writeParcelable(localBounds, 0 /* flags */); dest.writeParcelable(sourceContainerBounds, 0 /* flags */); + dest.writeParcelable(screenSpaceBounds, 0 /* flags */); dest.writeParcelable(windowConfiguration, 0 /* flags */); dest.writeBoolean(isNotInRecents); dest.writeParcelable(startLeash, 0 /* flags */); @@ -229,6 +257,8 @@ public class RemoteAnimationTarget implements Parcelable { pw.print(" prefixOrderIndex="); pw.print(prefixOrderIndex); pw.print(" position="); position.printShortString(pw); pw.print(" sourceContainerBounds="); sourceContainerBounds.printShortString(pw); + pw.print(" screenSpaceBounds="); screenSpaceBounds.printShortString(pw); + pw.print(" localBounds="); localBounds.printShortString(pw); pw.println(); pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration); pw.print(prefix); pw.print("leash="); pw.println(leash); @@ -245,6 +275,8 @@ public class RemoteAnimationTarget implements Parcelable { proto.write(PREFIX_ORDER_INDEX, prefixOrderIndex); position.dumpDebug(proto, POSITION); sourceContainerBounds.dumpDebug(proto, SOURCE_CONTAINER_BOUNDS); + screenSpaceBounds.dumpDebug(proto, SCREEN_SPACE_BOUNDS); + localBounds.dumpDebug(proto, LOCAL_BOUNDS); windowConfiguration.dumpDebug(proto, WINDOW_CONFIGURATION); if (startLeash != null) { startLeash.dumpDebug(proto, START_LEASH); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index fb7c04afeab0..1f7c3504560f 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -605,7 +605,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mTmpRect.set(0, 0, mSurfaceWidth, mSurfaceHeight); } SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(this); - applier.scheduleApply( + applier.scheduleApply(false /* earlyWakeup */, new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(mSurfaceControl) .withWindowCrop(mTmpRect) .build()); diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index 206e4f936edf..9c97f3e5b503 100644 --- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -53,10 +53,11 @@ public class SyncRtSurfaceTransactionApplier { /** * Schedules applying surface parameters on the next frame. * + * @param earlyWakeup Whether to set {@link Transaction#setEarlyWakeup()} on transaction. * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into * this method to avoid synchronization issues. */ - public void scheduleApply(final SurfaceParams... params) { + public void scheduleApply(boolean earlyWakeup, final SurfaceParams... params) { if (mTargetViewRootImpl == null) { return; } @@ -66,7 +67,7 @@ public class SyncRtSurfaceTransactionApplier { return; } Transaction t = new Transaction(); - applyParams(t, frame, params); + applyParams(t, frame, earlyWakeup, params); }); // Make sure a frame gets scheduled. @@ -77,10 +78,12 @@ public class SyncRtSurfaceTransactionApplier { * Applies surface parameters on the next frame. * @param t transaction to apply all parameters in. * @param frame frame to synchronize to. Set -1 when sync is not required. + * @param earlyWakeup Whether to set {@link Transaction#setEarlyWakeup()} on transaction. * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into * this method to avoid synchronization issues. */ - void applyParams(Transaction t, long frame, final SurfaceParams... params) { + void applyParams(Transaction t, long frame, boolean earlyWakeup, + final SurfaceParams... params) { for (int i = params.length - 1; i >= 0; i--) { SurfaceParams surfaceParams = params[i]; SurfaceControl surface = surfaceParams.surface; @@ -89,7 +92,9 @@ public class SyncRtSurfaceTransactionApplier { } applyParams(t, surfaceParams, mTmpFloat9); } - t.setEarlyWakeup(); + if (earlyWakeup) { + t.setEarlyWakeup(); + } t.apply(); } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index b6c46be66761..7d4ec3dcf2e7 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3714,7 +3714,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) { final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo = AccessibilityNodeInfo.ExtraRenderingInfo.obtain(); - extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height); + extraRenderingInfo.setLayoutSize(getLayoutParams().width, getLayoutParams().height); info.setExtraRenderingInfo(extraRenderingInfo); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8d3cffc512c3..9e5298b04497 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1735,7 +1735,7 @@ public final class ViewRootImpl implements ViewParent, mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession) .setContainerLayer() .setName("Bounds for - " + getTitle().toString()) - .setParent(mSurfaceControl) + .setParent(getRenderSurfaceControl()) .build(); setBoundsLayerCrop(); mTransaction.show(mBoundsLayer).apply(); @@ -5699,9 +5699,9 @@ public final class ViewRootImpl implements ViewParent, mTranslator.translateEventInScreenToAppWindow(event); } - // Enter touch mode if event is coming from a touch screen device. + // Enter touch mode on down or scroll from any type of a device. final int action = event.getAction(); - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { ensureTouchMode(true); } diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index b7ca03798bbe..2ad557e6d9f6 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -20,7 +20,9 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; +import android.inputmethodservice.InputMethodService; import android.os.CancellationSignal; +import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.animation.Interpolator; @@ -212,4 +214,55 @@ public interface WindowInsetsController { * @hide */ InsetsState getState(); + + /** + * Adds a {@link OnControllableInsetsChangedListener} to the window insets controller. + * + * @param listener The listener to add. + * + * @see OnControllableInsetsChangedListener + * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener) + */ + void addOnControllableInsetsChangedListener( + @NonNull OnControllableInsetsChangedListener listener); + + /** + * Removes a {@link OnControllableInsetsChangedListener} from the window insets controller. + * + * @param listener The listener to remove. + * + * @see OnControllableInsetsChangedListener + * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener) + */ + void removeOnControllableInsetsChangedListener( + @NonNull OnControllableInsetsChangedListener listener); + + /** + * Listener to be notified when the set of controllable {@link InsetsType} controlled by a + * {@link WindowInsetsController} changes. + * <p> + * Once a {@link InsetsType} becomes controllable, the app will be able to control the window + * that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}. + * <p> + * Note: When listening to controllability of the {@link Type#ime}, + * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService} + * decides to cancel the show request. This could happen when there is a hardware keyboard + * attached. + * + * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener) + * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener) + */ + interface OnControllableInsetsChangedListener { + + /** + * Called when the set of controllable {@link InsetsType} changes. + * + * @param controller The controller for which the set of controllable {@link InsetsType}s + * are changing. + * @param typeMask Bitwise type-mask of the {@link InsetsType}s the controller is currently + * able to control. + */ + void onControllableInsetsChanged(@NonNull WindowInsetsController controller, + @InsetsType int typeMask); + } } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 6435b42efca0..4050da1b5cb1 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -18,8 +18,11 @@ package android.view; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; +import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import android.annotation.NonNull; import android.app.ResourcesManager; @@ -215,9 +218,9 @@ public final class WindowManagerImpl implements WindowManager { @Override public WindowMetrics getCurrentWindowMetrics() { final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext; - final Rect bound = getCurrentBounds(context); + final Rect bounds = getCurrentBounds(context); - return new WindowMetrics(toSize(bound), computeWindowInsets()); + return new WindowMetrics(toSize(bounds), computeWindowInsets(bounds)); } private static Rect getCurrentBounds(Context context) { @@ -228,7 +231,8 @@ public final class WindowManagerImpl implements WindowManager { @Override public WindowMetrics getMaximumWindowMetrics() { - return new WindowMetrics(toSize(getMaximumBounds()), computeWindowInsets()); + final Rect maxBounds = getMaximumBounds(); + return new WindowMetrics(toSize(maxBounds), computeWindowInsets(maxBounds)); } private Size toSize(Rect frame) { @@ -244,9 +248,8 @@ public final class WindowManagerImpl implements WindowManager { return new Rect(0, 0, displaySize.x, displaySize.y); } - private WindowInsets computeWindowInsets() { - // TODO(b/118118435): This can only be properly implemented - // once we flip the new insets mode flag. + // TODO(b/150095967): Set window type to LayoutParams + private WindowInsets computeWindowInsets(Rect bounds) { // Initialize params which used for obtaining all system insets. final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; @@ -257,21 +260,34 @@ public final class WindowManagerImpl implements WindowManager { params.setFitInsetsTypes(0); params.setFitInsetsSides(0); - return getWindowInsetsFromServer(params); + return getWindowInsetsFromServer(params, bounds); } - private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs) { + private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) { try { final Rect systemWindowInsets = new Rect(); final Rect stableInsets = new Rect(); final DisplayCutout.ParcelableWrapper displayCutout = new DisplayCutout.ParcelableWrapper(); - WindowManagerGlobal.getWindowManagerService().getWindowInsets(attrs, - mContext.getDisplayId(), systemWindowInsets, stableInsets, displayCutout); - return new WindowInsets.Builder() - .setSystemWindowInsets(Insets.of(systemWindowInsets)) - .setStableInsets(Insets.of(stableInsets)) - .setDisplayCutout(displayCutout.get()).build(); + final InsetsState insetsState = new InsetsState(); + final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService() + .getWindowInsets(attrs, mContext.getDisplayId(), systemWindowInsets, + stableInsets, displayCutout, insetsState); + final boolean isScreenRound = + mContext.getResources().getConfiguration().isScreenRound(); + if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { + return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/, + isScreenRound, alwaysConsumeSystemBars, displayCutout.get(), + systemWindowInsets, stableInsets, SOFT_INPUT_ADJUST_NOTHING, + SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */); + } else { + return new WindowInsets.Builder() + .setAlwaysConsumeSystemBars(alwaysConsumeSystemBars) + .setRound(isScreenRound) + .setSystemWindowInsets(Insets.of(systemWindowInsets)) + .setStableInsets(Insets.of(stableInsets)) + .setDisplayCutout(displayCutout.get()).build(); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index f2cbf895dfb6..5fccf4031008 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -559,14 +559,6 @@ public class AccessibilityNodeInfo implements Parcelable { public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT"; - /** - * Argument to represent the IME action Id to press the returning key on a node. - * For use with R.id.accessibilityActionImeEnter - * @hide - */ - public static final String ACTION_ARGUMENT_IME_ACTION_ID_INT = - "android.view.accessibility.action.ARGUMENT_IME_ACTION_ID_INT"; - // Focus types /** @@ -649,7 +641,7 @@ public class AccessibilityNodeInfo implements Parcelable { * argument. * <p> * The data can be retrieved from the {@link ExtraRenderingInfo} returned by - * {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutParams}, + * {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutSize}, * {@link ExtraRenderingInfo#getTextSizeInPx()} and * {@link ExtraRenderingInfo#getTextSizeUnit()}. For layout params, it is supported by both * {@link TextView} and {@link ViewGroup}. For text size and unit, it is only supported by @@ -657,7 +649,6 @@ public class AccessibilityNodeInfo implements Parcelable { * * @see #refreshWithExtraData(String, Bundle) */ - public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY"; @@ -1038,8 +1029,8 @@ public class AccessibilityNodeInfo implements Parcelable { * with this mechanism is generally expensive to retrieve, so should only be * requested when needed. See * {@link #EXTRA_DATA_RENDERING_INFO_KEY}, - * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and - * {@link #getAvailableExtraData()}. + * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY}, + * {@link #getAvailableExtraData()} and {@link #getExtraRenderingInfo()}. * @param args A bundle of arguments for the request. These depend on the particular request. * * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented @@ -2437,9 +2428,13 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Gets the conformance info if the node is meant to be refreshed with extra data. + * Gets the {@link ExtraRenderingInfo extra rendering info} if the node is meant to be + * refreshed with extra data to examine rendering related accessibility issues. + * + * @return The {@link ExtraRenderingInfo extra rendering info}. * - * @return The conformance info. + * @see #EXTRA_DATA_RENDERING_INFO_KEY + * @see #refreshWithExtraData(String, Bundle) */ @Nullable public ExtraRenderingInfo getExtraRenderingInfo() { @@ -2447,14 +2442,15 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Sets the conformance info if the node is meant to be refreshed with extra data. + * Sets the extra rendering info, <code>extraRenderingInfo<code/>, if the node is meant to be + * refreshed with extra data. * <p> * <strong>Note:</strong> Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> * - * @param extraRenderingInfo The conformance info. + * @param extraRenderingInfo The {@link ExtraRenderingInfo extra rendering info}. * @hide */ public void setExtraRenderingInfo(@NonNull ExtraRenderingInfo extraRenderingInfo) { @@ -3925,7 +3921,7 @@ public class AccessibilityNodeInfo implements Parcelable { } if (isBitSet(nonDefaultFields, fieldIndex++)) { - parcel.writeValue(mExtraRenderingInfo.getLayoutParams()); + parcel.writeValue(mExtraRenderingInfo.getLayoutSize()); parcel.writeFloat(mExtraRenderingInfo.getTextSizeInPx()); parcel.writeInt(mExtraRenderingInfo.getTextSizeUnit()); } @@ -4189,7 +4185,7 @@ public class AccessibilityNodeInfo implements Parcelable { if (isBitSet(nonDefaultFields, fieldIndex++)) { if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle(); mExtraRenderingInfo = ExtraRenderingInfo.obtain(); - mExtraRenderingInfo.mLayoutParams = (Size) parcel.readValue(null); + mExtraRenderingInfo.mLayoutSize = (Size) parcel.readValue(null); mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat(); mExtraRenderingInfo.mTextSizeUnit = parcel.readInt(); } @@ -4969,10 +4965,11 @@ public class AccessibilityNodeInfo implements Parcelable { new AccessibilityAction(R.id.accessibilityActionPressAndHold); /** - * Action to send an ime action which is from - * {@link android.view.inputmethod.EditorInfo#actionId}. This action would be + * Action to send an ime actionId which is from + * {@link android.view.inputmethod.EditorInfo#actionId}. This ime actionId sets by + * {@link TextView#setImeActionLabel(CharSequence, int)}, or it would be * {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED} if no specific - * actionId defined. A node should expose this action only for views that are currently + * actionId has set. A node should expose this action only for views that are currently * with input focus and editable. */ @NonNull public static final AccessibilityAction ACTION_IME_ENTER = @@ -5808,7 +5805,7 @@ public class AccessibilityNodeInfo implements Parcelable { private static final SynchronizedPool<ExtraRenderingInfo> sPool = new SynchronizedPool<>(MAX_POOL_SIZE); - private Size mLayoutParams; + private Size mLayoutSize; private float mTextSizeInPx = UNDEFINED_VALUE; private int mTextSizeUnit = UNDEFINED_VALUE; @@ -5828,32 +5825,36 @@ public class AccessibilityNodeInfo implements Parcelable { /** Obtains a pooled instance that is a clone of another one. */ private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) { ExtraRenderingInfo extraRenderingInfo = ExtraRenderingInfo.obtain(); - extraRenderingInfo.mLayoutParams = other.mLayoutParams; + extraRenderingInfo.mLayoutSize = other.mLayoutSize; extraRenderingInfo.mTextSizeInPx = other.mTextSizeInPx; extraRenderingInfo.mTextSizeUnit = other.mTextSizeUnit; return extraRenderingInfo; } /** - * Creates a new conformance info of a view, and this new instance is initialized from + * Creates a new rendering info of a view, and this new instance is initialized from * the given <code>other</code>. * * @param other The instance to clone. */ private ExtraRenderingInfo(@Nullable ExtraRenderingInfo other) { if (other != null) { - mLayoutParams = other.mLayoutParams; + mLayoutSize = other.mLayoutSize; mTextSizeInPx = other.mTextSizeInPx; mTextSizeUnit = other.mTextSizeUnit; } } /** + * Gets the size object containing the height and the width of layout params if the node is + * a {@link ViewGroup} or a {@link TextView}, or null otherwise. Useful for accessibility + * scanning tool to understand whether the text is scalable and fits the view or not. + * * @return a {@link Size} stores layout height and layout width of the view, * or null otherwise. */ - public @Nullable Size getLayoutParams() { - return mLayoutParams; + public @Nullable Size getLayoutSize() { + return mLayoutSize; } /** @@ -5863,11 +5864,15 @@ public class AccessibilityNodeInfo implements Parcelable { * @param height The layout height. * @hide */ - public void setLayoutParams(int width, int height) { - mLayoutParams = new Size(width, height); + public void setLayoutSize(int width, int height) { + mLayoutSize = new Size(width, height); } /** + * Gets the text size if the node is a {@link TextView}, or -1 otherwise. Useful for + * accessibility scanning tool to understand whether the text is scalable and fits the view + * or not. + * * @return the text size of a {@code TextView}, or -1 otherwise. */ public float getTextSizeInPx() { @@ -5885,6 +5890,11 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Gets the text size unit if the node is a {@link TextView}, or -1 otherwise. + * Text size returned from {@link #getTextSizeInPx} in raw pixels may scale by factors and + * convert from other units. Useful for accessibility scanning tool to understand whether + * the text is scalable and fits the view or not. + * * @return the text size unit which type is {@link TypedValue#TYPE_DIMENSION} of a * {@code TextView}, or -1 otherwise. * @@ -5915,7 +5925,7 @@ public class AccessibilityNodeInfo implements Parcelable { } private void clear() { - mLayoutParams = null; + mLayoutSize = null; mTextSizeInPx = UNDEFINED_VALUE; mTextSizeUnit = UNDEFINED_VALUE; } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ce7cfa71e9a9..dda4e8b63a91 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -17,6 +17,7 @@ package android.view.autofill; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; @@ -63,6 +64,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityWindowInfo; import android.widget.EditText; +import android.widget.TextView; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -983,6 +985,10 @@ public final class AutofillManager { if (!isClientDisablingEnterExitEvent()) { final AutofillValue value = view.getAutofillValue(); + if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) { + flags |= FLAG_PASSWORD_INPUT_TYPE; + } + if (!isActiveLocked()) { // Starts new session. startSessionLocked(id, null, value, flags); @@ -1149,6 +1155,10 @@ public final class AutofillManager { } else { // don't notify entered when Activity is already in background if (!isClientDisablingEnterExitEvent()) { + if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) { + flags |= FLAG_PASSWORD_INPUT_TYPE; + } + if (!isActiveLocked()) { // Starts new session. startSessionLocked(id, bounds, null, flags); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index b7b54c8c74b9..b9889276ae0b 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -217,6 +217,15 @@ public final class ContentCaptureManager { public static final int DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED = 3; /** @hide */ + @IntDef(flag = false, value = { + DATA_SHARE_ERROR_UNKNOWN, + DATA_SHARE_ERROR_CONCURRENT_REQUEST, + DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DataShareError {} + + /** @hide */ public static final int RESULT_CODE_OK = 0; /** @hide */ public static final int RESULT_CODE_TRUE = 1; diff --git a/core/java/android/view/contentcapture/DataShareWriteAdapter.java b/core/java/android/view/contentcapture/DataShareWriteAdapter.java index 2beaededf8b1..3b5b756553d9 100644 --- a/core/java/android/view/contentcapture/DataShareWriteAdapter.java +++ b/core/java/android/view/contentcapture/DataShareWriteAdapter.java @@ -18,6 +18,7 @@ package android.view.contentcapture; import android.annotation.NonNull; import android.os.ParcelFileDescriptor; +import android.view.contentcapture.ContentCaptureManager.DataShareError; /** Adapter class used by apps to share data with the Content Capture service. */ public interface DataShareWriteAdapter { @@ -42,7 +43,7 @@ public interface DataShareWriteAdapter { * * @param errorCode the error code corresponding to an ERROR_* value. */ - default void onError(int errorCode) { + default void onError(@DataShareError int errorCode) { /* do nothing - stub */ } } diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index ff80ef7b2e94..f62a28ec0d07 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -268,17 +268,28 @@ public abstract class CookieManager { protected abstract boolean allowFileSchemeCookiesImpl(); /** - * Sets whether the application's {@link WebView} instances should send and - * accept cookies for file scheme URLs. - * Use of cookies with file scheme URLs is potentially insecure and turned - * off by default. - * Do not use this feature unless you can be sure that no unintentional - * sharing of cookie data can take place. + * Sets whether the application's {@link WebView} instances should send and accept cookies for + * file scheme URLs. + * <p> + * Use of cookies with file scheme URLs is potentially insecure and turned off by default. All + * {@code file://} URLs share all their cookies, which may lead to leaking private app cookies + * (ex. any malicious file can access cookies previously set by other (trusted) files). + * <p class="note"> + * Loading content via {@code file://} URLs is generally discouraged. See the note in + * {@link WebSettings#setAllowFileAccess}. + * Using <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html"> + * androidx.webkit.WebViewAssetLoader</a> to load files over {@code http(s)://} URLs allows + * the standard web security model to be used for setting and sharing cookies for local files. * <p> - * Note that calls to this method will have no effect if made after a - * {@link WebView} or CookieManager instance has been created. + * Note that calls to this method will have no effect if made after calling other + * {@link CookieManager} APIs. + * + * @deprecated This setting is not secure, please use + * <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html"> + * androidx.webkit.WebViewAssetLoader</a> instead. */ // Static for backward compatibility. + @Deprecated public static void setAcceptFileSchemeCookies(boolean accept) { getInstance().setAcceptFileSchemeCookiesImpl(accept); } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 53541f786da0..35dd5760d5ab 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -983,48 +983,63 @@ public abstract class WebSettings { public abstract void setJavaScriptEnabled(boolean flag); /** - * Sets whether JavaScript running in the context of a file scheme URL - * should be allowed to access content from any origin. This includes - * access to content from other file scheme URLs. See - * {@link #setAllowFileAccessFromFileURLs}. To enable the most restrictive, - * and therefore secure policy, this setting should be disabled. - * Note that this setting affects only JavaScript access to file scheme - * resources. Other access to such resources, for example, from image HTML - * elements, is unaffected. To prevent possible violation of same domain policy - * when targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, - * you should explicitly set this value to {@code false}. + * Sets whether cross-origin requests in the context of a file scheme URL should be allowed to + * access content from <i>any</i> origin. This includes access to content from other file + * scheme URLs or web contexts. Note that some access such as image HTML elements doesn't + * follow same-origin rules and isn't affected by this setting. + * <p> + * <b>Don't</b> enable this setting if you open files that may be created or altered by + * external sources. Enabling this setting allows malicious scripts loaded in a {@code file://} + * context to launch cross-site scripting attacks, either accessing arbitrary local files + * including WebView cookies, app private data or even credentials used on arbitrary web sites. + * <p class="note"> + * Loading content via {@code file://} URLs is generally discouraged. See the note in + * {@link #setAllowFileAccess}. * <p> * The default value is {@code true} for apps targeting - * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, - * and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} - * and above. - * - * @param flag whether JavaScript running in the context of a file scheme - * URL should be allowed to access content from any origin + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and {@code false} + * when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. To prevent + * possible violation of same domain policy when targeting + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, you should + * explicitly set this value to {@code false}. + * + * @param flag whether JavaScript running in the context of a file scheme URL should be allowed + * to access content from any origin + * @deprecated This setting is not secure, please use + * <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html"> + * androidx.webkit.WebViewAssetLoader</a> to load file content securely. */ + @Deprecated public abstract void setAllowUniversalAccessFromFileURLs(boolean flag); /** - * Sets whether JavaScript running in the context of a file scheme URL - * should be allowed to access content from other file scheme URLs. To - * enable the most restrictive, and therefore secure, policy this setting - * should be disabled. Note that the value of this setting is ignored if - * the value of {@link #getAllowUniversalAccessFromFileURLs} is {@code true}. - * Note too, that this setting affects only JavaScript access to file scheme - * resources. Other access to such resources, for example, from image HTML - * elements, is unaffected. To prevent possible violation of same domain policy - * when targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, - * you should explicitly set this value to {@code false}. + * Sets whether cross-origin requests in the context of a file scheme URL should be allowed to + * access content from other file scheme URLs. Note that some accesses such as image HTML + * elements don't follow same-origin rules and aren't affected by this setting. * <p> - * The default value is {@code true} for apps targeting - * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, - * and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} - * and above. + * <b>Don't</b> enable this setting if you open files that may be created or altered by + * external sources. Enabling this setting allows malicious scripts loaded in a {@code file://} + * context to access arbitrary local files including WebView cookies and app private data. + * <p class="note"> + * Loading content via {@code file://} URLs is generally discouraged. See the note in + * {@link #setAllowFileAccess}. + * <p> + * Note that the value of this setting is ignored if the value of + * {@link #getAllowUniversalAccessFromFileURLs} is {@code true}. The default value is + * {@code true} for apps targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} + * and below, and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} + * and above. To prevent possible violation of same domain policy when targeting + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, you should + * explicitly set this value to {@code false}. * * @param flag whether JavaScript running in the context of a file scheme * URL should be allowed to access content from other file * scheme URLs + * @deprecated This setting is not secure, please use + * <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html"> + * androidx.webkit.WebViewAssetLoader</a> to load file content securely. */ + @Deprecated public abstract void setAllowFileAccessFromFileURLs(boolean flag); /** diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 13c1f67ef85b..816612f1dcc7 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -4587,7 +4587,8 @@ public class Editor { protected int mHorizontalGravity; // Offsets the hotspot point up, so that cursor is not hidden by the finger when moving up private float mTouchOffsetY; - // Where the touch position should be on the handle to ensure a maximum cursor visibility + // Where the touch position should be on the handle to ensure a maximum cursor visibility. + // This is the distance in pixels from the top of the handle view. private float mIdealVerticalOffset; // Parent's (TextView) previous position in window private int mLastParentX, mLastParentY; @@ -4612,6 +4613,11 @@ public class Editor { // when magnifier is used. private float mTextViewScaleX; private float mTextViewScaleY; + /** + * The vertical distance in pixels from finger to the cursor Y while dragging. + * See {@link Editor.InsertionPointCursorController#getLineDuringDrag}. + */ + private final int mIdealFingerToCursorOffset; private HandleView(Drawable drawableLtr, Drawable drawableRtl, final int id) { super(mTextView.getContext()); @@ -4633,12 +4639,17 @@ public class Editor { final int handleHeight = getPreferredHeight(); mTouchOffsetY = -0.3f * handleHeight; mIdealVerticalOffset = 0.7f * handleHeight; + mIdealFingerToCursorOffset = (int)(mIdealVerticalOffset - mTouchOffsetY); } public float getIdealVerticalOffset() { return mIdealVerticalOffset; } + final int getIdealFingerToCursorOffset() { + return mIdealFingerToCursorOffset; + } + void setDrawables(final Drawable drawableLtr, final Drawable drawableRtl) { mDrawableLtr = drawableLtr; mDrawableRtl = drawableRtl; @@ -6123,36 +6134,34 @@ public class Editor { */ private int getLineDuringDrag(MotionEvent event) { final Layout layout = mTextView.getLayout(); - if (mTouchState.isOnHandle()) { - // The drag was initiated from the handle, so no need to apply the snap logic. See - // InsertionHandleView.touchThrough(). + if (mPrevLineDuringDrag == UNSET_LINE) { return getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, event.getY()); } + // In case of touch through on handle (when isOnHandle() returns true), event.getY() + // returns the midpoint of the cursor vertical bar, while event.getRawY() returns the + // finger location on the screen. See {@link InsertionHandleView#touchThrough}. + final float fingerY = mTouchState.isOnHandle() + ? event.getRawY() - mTextView.getLocationOnScreen()[1] + : event.getY(); + final float cursorY = fingerY - getHandle().getIdealFingerToCursorOffset(); + int line = getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, cursorY); if (mIsTouchSnappedToHandleDuringDrag) { - float cursorY = event.getY() - getHandle().getIdealVerticalOffset(); - return getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, cursorY); - } - int line = getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, event.getY()); - if (mPrevLineDuringDrag == UNSET_LINE || line <= mPrevLineDuringDrag) { - // User's finger is on the same line or moving up; continue positioning the cursor - // directly at the touch location. + // Just returns the line hit by cursor Y when already snapped. return line; } - // User's finger is moving downwards; delay jumping to the lower line to allow the - // touch to move to the handle. - float cursorY = event.getY() - getHandle().getIdealVerticalOffset(); - line = getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, cursorY); if (line < mPrevLineDuringDrag) { - return mPrevLineDuringDrag; + // The cursor Y aims too high & not yet snapped, check the finger Y. + // If finger Y is moving downwards, don't jump to lower line (until snap). + // If finger Y is moving upwards, can jump to upper line. + return Math.min(mPrevLineDuringDrag, + getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, fingerY)); } - // User's finger is now over the handle, at the ideal offset from the cursor. From now - // on, position the cursor higher up from the actual touch location so that the user's - // finger stays "snapped" to the handle. This provides better visibility of the text. + // The cursor Y aims not too high, so snap! mIsTouchSnappedToHandleDuringDrag = true; if (TextView.DEBUG_CURSOR) { logCursor("InsertionPointCursorController", - "snapped touch to handle: eventY=%d, cursorY=%d, mLastLine=%d, line=%d", - (int) event.getY(), (int) cursorY, mPrevLineDuringDrag, line); + "snapped touch to handle: fingerY=%d, cursorY=%d, mLastLine=%d, line=%d", + (int) fingerY, (int) cursorY, mPrevLineDuringDrag, line); } return line; } @@ -6252,7 +6261,7 @@ public class Editor { } } - private InsertionHandleView getHandle() { + public InsertionHandleView getHandle() { if (mHandle == null) { loadHandleDrawables(false /* overwrite */); mHandle = new InsertionHandleView(mSelectHandleCenter); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f3243aaf5b7d..2168018e12be 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6607,6 +6607,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTransformation instanceof PasswordTransformationMethod; } + /** + * Returns true if the current inputType is any type of password. + * + * @hide + */ + public boolean isAnyPasswordInputType() { + final int inputType = getInputType(); + return isPasswordInputType(inputType) || isVisiblePasswordInputType(inputType); + } + static boolean isPasswordInputType(int inputType) { final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION); @@ -11763,13 +11773,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (isTextEditable() && isFocused()) { CharSequence imeActionLabel = mContext.getResources().getString( com.android.internal.R.string.keyboardview_keycode_enter); - if (getImeActionId() != 0 && getImeActionLabel() != null) { + if (getImeActionLabel() != null) { imeActionLabel = getImeActionLabel(); - final int imeActionId = getImeActionId(); - // put ime action id into the extra data with ACTION_ARGUMENT_IME_ACTION_ID_INT. - final Bundle argument = info.getExtras(); - argument.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_IME_ACTION_ID_INT, - imeActionId); } AccessibilityNodeInfo.AccessibilityAction action = new AccessibilityNodeInfo.AccessibilityAction( @@ -11881,7 +11886,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) { final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo = AccessibilityNodeInfo.ExtraRenderingInfo.obtain(); - extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height); + extraRenderingInfo.setLayoutSize(getLayoutParams().width, getLayoutParams().height); extraRenderingInfo.setTextSizeInPx(getTextSize()); extraRenderingInfo.setTextSizeUnit(getTextSizeUnit()); info.setExtraRenderingInfo(extraRenderingInfo); @@ -12100,13 +12105,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } return true; case R.id.accessibilityActionImeEnter: { if (isFocused() && isTextEditable()) { - final int imeActionId = (arguments != null) ? arguments.getInt( - AccessibilityNodeInfo.ACTION_ARGUMENT_IME_ACTION_ID_INT, - EditorInfo.IME_ACTION_UNSPECIFIED) - : EditorInfo.IME_ACTION_UNSPECIFIED; - if (imeActionId == getImeActionId()) { - onEditorAction(imeActionId); - } + onEditorAction(getImeActionId()); } } return true; default: { diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 5cdcab029877..54ea57a6cae4 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -56,6 +56,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -133,7 +134,7 @@ public class AccessibilityShortcutController { // Keep track of state of shortcut settings final ContentObserver co = new ContentObserver(handler) { @Override - public void onChange(boolean selfChange, Uri uri, int userId) { + public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) { if (userId == mUserId) { onSettingsChanged(); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index c487e960854b..c099301d2043 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2398,17 +2398,20 @@ public class ChooserActivity extends ResolverActivity implements } final int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight(); - if (mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) { - gridAdapter.calculateChooserTargetWidth(availableWidth); - return; - } - - if (gridAdapter.consumeLayoutRequest() + boolean isLayoutUpdated = gridAdapter.consumeLayoutRequest() || gridAdapter.calculateChooserTargetWidth(availableWidth) || recyclerView.getAdapter() == null - || mLastNumberOfChildren != recyclerView.getChildCount() - || availableWidth != mCurrAvailableWidth) { + || availableWidth != mCurrAvailableWidth; + if (isLayoutUpdated + || mLastNumberOfChildren != recyclerView.getChildCount()) { mCurrAvailableWidth = availableWidth; + if (isLayoutUpdated + && mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) { + // This fixes b/150936654 - empty work tab in share sheet when swiping + mChooserMultiProfilePagerAdapter.getActiveAdapterView() + .setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter()); + return; + } getMainThreadHandler().post(() -> { if (mResolverDrawerLayout == null || gridAdapter == null) { diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 4c203d394759..523ed6fa9a4f 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -164,6 +164,30 @@ interface IPlatformCompat boolean clearOverride(long changeId, String packageName); /** + * Enable all compatibility changes which have enabledAfterTargetSdk == + * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the + * changes to take effect. + * + * @param packageName The package name of the app whose compatibility changes will be enabled. + * @param targetSdkVersion The targetSdkVersion for filtering the changes to be enabled. + * + * @return The number of changes that were enabled. + */ + int enableTargetSdkChanges(in String packageName, int targetSdkVersion); + + /** + * Disable all compatibility changes which have enabledAfterTargetSdk == + * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the + * changes to take effect. + * + * @param packageName The package name of the app whose compatibility changes will be disabled. + * @param targetSdkVersion The targetSdkVersion for filtering the changes to be disabled. + * + * @return The number of changes that were disabled. + */ + int disableTargetSdkChanges(in String packageName, int targetSdkVersion); + + /** * Revert overrides to compatibility changes. Kills the app to allow the changes to take effect. * * @param packageName The package name of the app whose overrides will be cleared. diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java index bed41b37ac81..9a78ad2011cf 100644 --- a/core/java/com/android/internal/compat/OverrideAllowedState.java +++ b/core/java/com/android/internal/compat/OverrideAllowedState.java @@ -50,7 +50,7 @@ public final class OverrideAllowedState implements Parcelable { public static final int DISABLED_NOT_DEBUGGABLE = 1; /** * Change cannot be overridden, due to the build being non-debuggable and the change being - * non-targetSdk. + * enabled regardless of targetSdk. */ public static final int DISABLED_NON_TARGET_SDK = 2; /** @@ -159,4 +159,28 @@ public final class OverrideAllowedState implements Parcelable { && appTargetSdk == otherState.appTargetSdk && changeIdTargetSdk == otherState.changeIdTargetSdk; } + + private String stateName() { + switch (state) { + case ALLOWED: + return "ALLOWED"; + case DISABLED_NOT_DEBUGGABLE: + return "DISABLED_NOT_DEBUGGABLE"; + case DISABLED_NON_TARGET_SDK: + return "DISABLED_NON_TARGET_SDK"; + case DISABLED_TARGET_SDK_TOO_HIGH: + return "DISABLED_TARGET_SDK_TOO_HIGH"; + case PACKAGE_DOES_NOT_EXIST: + return "PACKAGE_DOES_NOT_EXIST"; + case LOGGING_ONLY_CHANGE: + return "LOGGING_ONLY_CHANGE"; + } + return "UNKNOWN"; + } + + @Override + public String toString() { + return "OverrideAllowedState(state=" + stateName() + "; appTargetSdk=" + appTargetSdk + + "; changeIdTargetSdk=" + changeIdTargetSdk + ")"; + } } diff --git a/core/java/com/android/internal/logging/InstanceId.java b/core/java/com/android/internal/logging/InstanceId.java index 85643fcffa2f..c90d851201a2 100644 --- a/core/java/com/android/internal/logging/InstanceId.java +++ b/core/java/com/android/internal/logging/InstanceId.java @@ -48,6 +48,17 @@ public final class InstanceId implements Parcelable { return mId; } + /** + * Create a fake instance ID for testing purposes. Not for production use. See also + * InstanceIdSequenceFake, which is a testing replacement for InstanceIdSequence. + * @param id The ID you want to assign. + * @return new InstanceId. + */ + @VisibleForTesting + public static InstanceId fakeInstanceId(int id) { + return new InstanceId(id); + } + @Override public int hashCode() { return mId; diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java index f8c0d9e4a27e..fdcc8a8c9cbf 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java @@ -29,6 +29,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -99,7 +100,7 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { } @Override - public void onChange(boolean selfChange, Uri uri, int userId) { + public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) { updateReader(); } diff --git a/core/java/com/android/internal/util/NotificationMessagingUtil.java b/core/java/com/android/internal/util/NotificationMessagingUtil.java index bf796cddd89e..28994fd52126 100644 --- a/core/java/com/android/internal/util/NotificationMessagingUtil.java +++ b/core/java/com/android/internal/util/NotificationMessagingUtil.java @@ -28,6 +28,7 @@ import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; +import java.util.Collection; import java.util.Objects; /** @@ -77,8 +78,8 @@ public class NotificationMessagingUtil { private final ContentObserver mSmsContentObserver = new ContentObserver( new Handler(Looper.getMainLooper())) { @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - if (Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING).equals(uri)) { + public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) { + if (uris.contains(Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING))) { cacheDefaultSmsApp(userId); } } diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 3378c073eace..d40924b603a5 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -82,6 +82,9 @@ public class SystemConfig { // property for runtime configuration differentiation private static final String SKU_PROPERTY = "ro.boot.product.hardware.sku"; + // property for runtime configuration differentiation in vendor + private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku"; + // Group-ids that are given to all packages as read from etc/permissions/*.xml. int[] mGlobalGids; @@ -468,6 +471,17 @@ public class SystemConfig { readPermissions(Environment.buildPath( Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag); + String vendorSkuProperty = SystemProperties.get(VENDOR_SKU_PROPERTY, ""); + if (!vendorSkuProperty.isEmpty()) { + String vendorSkuDir = "sku_" + vendorSkuProperty; + readPermissions(Environment.buildPath( + Environment.getVendorDirectory(), "etc", "sysconfig", vendorSkuDir), + vendorPermissionFlag); + readPermissions(Environment.buildPath( + Environment.getVendorDirectory(), "etc", "permissions", vendorSkuDir), + vendorPermissionFlag); + } + // Allow ODM to customize system configs as much as Vendor, because /odm is another // vendor partition other than /vendor. int odmPermissionFlag = vendorPermissionFlag; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 4e139a34c555..2128f99ff609 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -134,6 +134,11 @@ static jmethodID gCreateSystemServerClassLoader; static bool gIsSecurityEnforced = true; /** + * True if the app process is running in its mount namespace. + */ +static bool gInAppMountNamespace = false; + +/** * The maximum number of characters (not including a null terminator) that a * process name may contain. */ @@ -548,6 +553,17 @@ static void SetGids(JNIEnv* env, jintArray managed_gids, fail_fn_t fail_fn) { } } +static void ensureInAppMountNamespace(fail_fn_t fail_fn) { + if (gInAppMountNamespace) { + // In app mount namespace already + return; + } + if (unshare(CLONE_NEWNS) == -1) { + fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno))); + } + gInAppMountNamespace = true; +} + // Sets the resource limits via setrlimit(2) for the values in the // two-dimensional array of integers that's passed in. The second dimension // contains a tuple of length 3: (resource, rlim_cur, rlim_max). nullptr is @@ -811,9 +827,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, } // Create a second private mount namespace for our process - if (unshare(CLONE_NEWNS) == -1) { - fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno))); - } + ensureInAppMountNamespace(fail_fn); // Handle force_mount_namespace with MOUNT_EXTERNAL_NONE. if (mount_mode == MOUNT_EXTERNAL_NONE) { @@ -1319,6 +1333,7 @@ static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, if ((size % 3) != 0) { fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size)); } + ensureInAppMountNamespace(fail_fn); // Mount tmpfs on all possible data directories, so app no longer see the original apps data. char internalCePath[PATH_MAX]; diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto index b4f3d1ea5ae4..22f249820b11 100644 --- a/core/proto/android/bluetooth/enums.proto +++ b/core/proto/android/bluetooth/enums.proto @@ -40,6 +40,7 @@ enum EnableDisableReasonEnum { ENABLE_DISABLE_REASON_CRASH = 7; ENABLE_DISABLE_REASON_USER_SWITCH = 8; ENABLE_DISABLE_REASON_RESTORE_USER_SETTING = 9; + ENABLE_DISABLE_REASON_FACTORY_RESET = 10; } enum DirectionEnum { diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index e8a0b46e8430..60892557891b 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -53,10 +53,10 @@ message RootWindowContainerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional WindowContainerProto window_container = 1; - repeated DisplayContentProto displays = 2; + repeated DisplayContentProto displays = 2 [deprecated=true]; reserved 3; // IdentifierProto windows /* window references in top down z order */ - repeated WindowStateProto windows = 4; + repeated WindowStateProto windows = 4 [deprecated=true]; optional KeyguardControllerProto keyguard_controller = 5; // Whether or not the home activity is the recents activity. This is needed for the CTS tests to // know what activity types to check for when invoking splitscreen multi-window. @@ -192,7 +192,8 @@ message DisplayContentProto { optional bool single_task_instance = 22; optional int32 focused_root_task_id = 23; optional .com.android.server.wm.IdentifierProto resumed_activity = 24; - repeated TaskProto tasks = 25; + repeated TaskProto tasks = 25 [deprecated=true]; + optional bool display_ready = 26; } /* represents DisplayArea object */ @@ -201,7 +202,7 @@ message DisplayAreaProto { optional WindowContainerProto window_container = 1; optional string name = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; - repeated DisplayAreaChildProto children = 3; + repeated DisplayAreaChildProto children = 3 [deprecated=true]; } /* represents a generic child of a DisplayArea */ @@ -254,8 +255,8 @@ message TaskProto { optional int32 surface_width = 8; optional int32 surface_height = 9; - repeated TaskProto tasks = 10; - repeated ActivityRecordProto activities = 11; + repeated TaskProto tasks = 10 [deprecated=true]; + repeated ActivityRecordProto activities = 11 [deprecated=true]; optional .com.android.server.wm.IdentifierProto resumed_activity = 12; optional string real_activity = 13; @@ -321,7 +322,7 @@ message WindowTokenProto { optional WindowContainerProto window_container = 1; optional int32 hash_code = 2; - repeated WindowStateProto windows = 3; + repeated WindowStateProto windows = 3 [deprecated=true]; optional bool waiting_to_show = 5; optional bool paused = 6; } @@ -346,7 +347,7 @@ message WindowStateProto { optional .android.graphics.RectProto surface_insets = 12; optional WindowStateAnimatorProto animator = 13; optional bool animating_exit = 14; - repeated WindowStateProto child_windows = 15; + repeated WindowStateProto child_windows = 15 [deprecated=true]; optional .android.graphics.RectProto surface_position = 16; optional int32 requested_width = 18; optional int32 requested_height = 19; @@ -426,6 +427,32 @@ message WindowContainerProto { optional int32 orientation = 2; optional bool visible = 3; optional SurfaceAnimatorProto surface_animator = 4; + repeated WindowContainerChildProto children = 5; +} + +/* represents a generic child of a WindowContainer */ +message WindowContainerChildProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + /* A window container can have multiple children of different types stored as + * a WindowContainerChildProto but each instance of WindowContainerChildProto + * can only contain a single type. + */ + /* We do not know the derived typ and the class is dumped + * as a WindowContainer */ + optional WindowContainerProto window_container = 2; + /* represents a DisplayContent child */ + optional DisplayContentProto display_content = 3; + /* represents a DisplayArea child */ + optional DisplayAreaProto display_area = 4; + /* represents a Task child */ + optional TaskProto task = 5; + /* represents an ActivityRecord child */ + optional ActivityRecordProto activity = 6; + /* represents a WindowToken child */ + optional WindowTokenProto window_token = 7; + /* represents a WindowState child */ + optional WindowStateProto window = 8; } /* represents ConfigurationContainer */ diff --git a/core/proto/android/view/remote_animation_target.proto b/core/proto/android/view/remote_animation_target.proto index 24d27858bd20..150493d251c6 100644 --- a/core/proto/android/view/remote_animation_target.proto +++ b/core/proto/android/view/remote_animation_target.proto @@ -44,4 +44,6 @@ message RemoteAnimationTargetProto { optional .android.app.WindowConfigurationProto window_configuration = 10; optional .android.view.SurfaceControlProto start_leash = 11; optional .android.graphics.RectProto start_bounds = 12; + optional .android.graphics.RectProto local_bounds = 13; + optional .android.graphics.RectProto screen_space_bounds = 14; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 885117018c50..0dd3ad639aa1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1187,6 +1187,16 @@ android:description="@string/permdesc_callCompanionApp" android:protectionLevel="normal" /> + <!-- Exempt this uid from restrictions to background audio recoding + <p>Protection level: signature|privileged + @hide + @SystemApi + --> + <permission android:name="android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS" + android:label="@string/permlab_exemptFromAudioRecordRestrictions" + android:description="@string/permdesc_exemptFromAudioRecordRestrictions" + android:protectionLevel="signature|privileged" /> + <!-- Allows a calling app to continue a call which was started in another app. An example is a video calling app that wants to continue a voice call on the user's mobile network.<p> When the handover of a call from one app to another takes place, there are two devices @@ -1305,7 +1315,7 @@ android:description="@string/permdesc_camera" android:protectionLevel="dangerous|instant" /> - <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access + <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access system only camera devices. <p>Protection level: system|signature @hide --> @@ -1315,6 +1325,15 @@ android:description="@string/permdesc_systemCamera" android:protectionLevel="system|signature" /> + <!-- Allows receiving the camera service notifications when a camera is opened + (by a certain application package) or closed. + @hide --> + <permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER" + android:permissionGroup="android.permission-group.UNDEFINED" + android:label="@string/permlab_cameraOpenCloseListener" + android:description="@string/permdesc_cameraOpenCloseListener" + android:protectionLevel="signature" /> + <!-- ====================================================================== --> <!-- Permissions for accessing the device sensors --> <!-- ====================================================================== --> @@ -3726,7 +3745,8 @@ <permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY" android:protectionLevel="signature|installer" /> - <!-- @hide Allows an application to upgrade runtime permissions. --> + <!-- @SystemApi @TestApi Allows an application to upgrade runtime permissions. + @hide --> <permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" android:protectionLevel="signature" /> diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml index e17fc8a16b9f..b754e0cfc022 100644 --- a/core/res/res/layout/resolver_list.xml +++ b/core/res/res/layout/resolver_list.xml @@ -100,8 +100,7 @@ android:layout_width="match_parent" android:layout_height="1dp" android:background="?attr/colorBackgroundFloating" - android:foreground="?attr/dividerVertical" - android:layout_marginBottom="@dimen/resolver_tab_divider_bottom_padding"/> + android:foreground="?attr/dividerVertical"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml index 0d4523add3cb..06a7633ce3f5 100644 --- a/core/res/res/layout/resolver_list_with_default.xml +++ b/core/res/res/layout/resolver_list_with_default.xml @@ -183,8 +183,7 @@ android:layout_width="match_parent" android:layout_height="1dp" android:background="?attr/colorBackgroundFloating" - android:foreground="?attr/dividerVertical" - android:layout_marginBottom="@dimen/resolver_tab_divider_bottom_padding"/> + android:foreground="?attr/dividerVertical"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index caec9edc7f1c..632c400d4d49 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1631,12 +1631,9 @@ <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Verhoog volume bo aanbevole vlak?\n\nOm lang tydperke teen hoë volume te luister, kan jou gehoor beskadig."</string> <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gebruik toeganklikheidkortpad?"</string> <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wanneer die kortpad aan is, sal \'n toeganklikheidkenmerk begin word as albei volumeknoppies 3 sekondes lank gedruk word."</string> - <!-- no translation found for accessibility_select_shortcut_menu_title (7310194076629867377) --> - <skip /> - <!-- no translation found for accessibility_edit_shortcut_menu_button_title (6096484087245145325) --> - <skip /> - <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (4849108668454490699) --> - <skip /> + <string name="accessibility_select_shortcut_menu_title" msgid="7310194076629867377">"Tik op die toeganklikheidprogram wat jy wil gebruik"</string> + <string name="accessibility_edit_shortcut_menu_button_title" msgid="6096484087245145325">"Kies programme wat jy met toeganklikheidknoppie wil gebruik"</string> + <string name="accessibility_edit_shortcut_menu_volume_title" msgid="4849108668454490699">"Kies programme wat jy met die volumesleutelkortpad wil gebruik"</string> <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Wysig kortpaaie"</string> <string name="cancel_accessibility_shortcut_menu_button" msgid="1817413122335452474">"Kanselleer"</string> <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Skakel kortpad af"</string> @@ -2033,29 +2030,19 @@ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in die BEPERK-groep geplaas"</string> <string name="resolver_personal_tab" msgid="2051260504014442073">"Persoonlik"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"Werk"</string> - <!-- no translation found for resolver_personal_tab_accessibility (5739524949153091224) --> - <skip /> - <!-- no translation found for resolver_work_tab_accessibility (4753168230363802734) --> - <skip /> + <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Persoonlike aansig"</string> + <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Werkaansig"</string> <string name="resolver_cant_share_with_work_apps" msgid="7539495559434146897">"Kan nie met werkprogramme deel nie"</string> <string name="resolver_cant_share_with_personal_apps" msgid="8020581735267157241">"Kan nie met persoonlike programme deel nie"</string> - <!-- no translation found for resolver_cant_share_cross_profile_explanation (5556640604460901386) --> - <skip /> - <!-- no translation found for resolver_cant_access_work_apps (375634344111233790) --> - <skip /> - <!-- no translation found for resolver_cant_access_work_apps_explanation (3958762224516867388) --> - <skip /> - <!-- no translation found for resolver_cant_access_personal_apps (1953215925406474177) --> - <skip /> - <!-- no translation found for resolver_cant_access_personal_apps_explanation (1725572276741281136) --> - <skip /> - <!-- no translation found for resolver_turn_on_work_apps_share (619263911204978175) --> - <skip /> - <!-- no translation found for resolver_turn_on_work_apps_view (3073389230905543680) --> - <skip /> + <string name="resolver_cant_share_cross_profile_explanation" msgid="5556640604460901386">"Jou IT-admin het deling tussen persoonlike en werkprofiele geblokkeer"</string> + <string name="resolver_cant_access_work_apps" msgid="375634344111233790">"Kan nie toegang tot werkprogramme kry nie"</string> + <string name="resolver_cant_access_work_apps_explanation" msgid="3958762224516867388">"Jou IT-admin laat jou nie toe om persoonlike inhoud in werkprogramme te bekyk nie"</string> + <string name="resolver_cant_access_personal_apps" msgid="1953215925406474177">"Kan nie toegang tot persoonlike programme kry nie"</string> + <string name="resolver_cant_access_personal_apps_explanation" msgid="1725572276741281136">"Jou IT-admin laat jou nie toe om werkinhoud in persoonlike programme te bekyk nie"</string> + <string name="resolver_turn_on_work_apps_share" msgid="619263911204978175">"Skakel werkprofiel aan om inhoud te deel"</string> + <string name="resolver_turn_on_work_apps_view" msgid="3073389230905543680">"Skakel werkprofiel aan om inhoud te bekyk"</string> <string name="resolver_no_apps_available" msgid="7710339903040989654">"Geen programme beskikbaar nie"</string> - <!-- no translation found for resolver_switch_on_work (2873009160846966379) --> - <skip /> + <string name="resolver_switch_on_work" msgid="2873009160846966379">"Skakel aan"</string> <string name="permlab_accessCallAudio" msgid="1682957511874097664">"Neem oudio in telefonie-oproepe op of speel dit"</string> <string name="permdesc_accessCallAudio" msgid="8448360894684277823">"Laat hierdie program, indien dit as die verstekbellerprogram aangewys is, toe om oudio in telefonie-oproepe op te neem of te speel."</string> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 63497aa692db..1a7f30159b68 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1631,12 +1631,9 @@ <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string> <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ユーザー補助機能のショートカットの使用"</string> <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ショートカットが ON の場合、両方の音量ボタンを 3 秒ほど長押しするとユーザー補助機能が起動します。"</string> - <!-- no translation found for accessibility_select_shortcut_menu_title (7310194076629867377) --> - <skip /> - <!-- no translation found for accessibility_edit_shortcut_menu_button_title (6096484087245145325) --> - <skip /> - <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (4849108668454490699) --> - <skip /> + <string name="accessibility_select_shortcut_menu_title" msgid="7310194076629867377">"使用するユーザー補助アプリをタップ"</string> + <string name="accessibility_edit_shortcut_menu_button_title" msgid="6096484087245145325">"ユーザー補助機能ボタンで使用できるアプリを選択"</string> + <string name="accessibility_edit_shortcut_menu_volume_title" msgid="4849108668454490699">"音量キーのショートカットで使用できるアプリを選択"</string> <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ショートカットの編集"</string> <string name="cancel_accessibility_shortcut_menu_button" msgid="1817413122335452474">"キャンセル"</string> <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"ショートカットを OFF にする"</string> @@ -2033,29 +2030,19 @@ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> は RESTRICTED バケットに移動しました。"</string> <string name="resolver_personal_tab" msgid="2051260504014442073">"個人用"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"仕事用"</string> - <!-- no translation found for resolver_personal_tab_accessibility (5739524949153091224) --> - <skip /> - <!-- no translation found for resolver_work_tab_accessibility (4753168230363802734) --> - <skip /> + <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"個人用ビュー"</string> + <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"仕事用ビュー"</string> <string name="resolver_cant_share_with_work_apps" msgid="7539495559434146897">"仕事用アプリと共有できません"</string> <string name="resolver_cant_share_with_personal_apps" msgid="8020581735267157241">"個人用アプリと共有できません"</string> - <!-- no translation found for resolver_cant_share_cross_profile_explanation (5556640604460901386) --> - <skip /> - <!-- no translation found for resolver_cant_access_work_apps (375634344111233790) --> - <skip /> - <!-- no translation found for resolver_cant_access_work_apps_explanation (3958762224516867388) --> - <skip /> - <!-- no translation found for resolver_cant_access_personal_apps (1953215925406474177) --> - <skip /> - <!-- no translation found for resolver_cant_access_personal_apps_explanation (1725572276741281136) --> - <skip /> - <!-- no translation found for resolver_turn_on_work_apps_share (619263911204978175) --> - <skip /> - <!-- no translation found for resolver_turn_on_work_apps_view (3073389230905543680) --> - <skip /> + <string name="resolver_cant_share_cross_profile_explanation" msgid="5556640604460901386">"IT 管理者が個人用プロファイルと仕事用プロファイルの間の共有をブロックしました"</string> + <string name="resolver_cant_access_work_apps" msgid="375634344111233790">"仕事用アプリにアクセスできません"</string> + <string name="resolver_cant_access_work_apps_explanation" msgid="3958762224516867388">"IT 管理者は個人用コンテンツを仕事用アプリで表示することを許可していません"</string> + <string name="resolver_cant_access_personal_apps" msgid="1953215925406474177">"個人用アプリにアクセスできません"</string> + <string name="resolver_cant_access_personal_apps_explanation" msgid="1725572276741281136">"IT 管理者は仕事用コンテンツを個人用アプリで表示することを許可していません"</string> + <string name="resolver_turn_on_work_apps_share" msgid="619263911204978175">"コンテンツを共有するには、仕事用プロファイルをオンにしてください"</string> + <string name="resolver_turn_on_work_apps_view" msgid="3073389230905543680">"コンテンツを表示するには、仕事用プロファイルをオンにしてください"</string> <string name="resolver_no_apps_available" msgid="7710339903040989654">"利用できるアプリはありません"</string> - <!-- no translation found for resolver_switch_on_work (2873009160846966379) --> - <skip /> + <string name="resolver_switch_on_work" msgid="2873009160846966379">"オンにする"</string> <string name="permlab_accessCallAudio" msgid="1682957511874097664">"通話中に録音または音声の再生を行う"</string> <string name="permdesc_accessCallAudio" msgid="8448360894684277823">"デフォルトの電話アプリケーションとして割り当てられている場合、このアプリに通話中の録音または音声の再生を許可します。"</string> </resources> diff --git a/core/res/res/values-mcc219/config.xml b/core/res/res/values-mcc219/config.xml deleted file mode 100644 index 7ae82fa91c95..000000000000 --- a/core/res/res/values-mcc219/config.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. --> -<resources> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array name="config_twoDigitNumberPattern"> - <item>"92"</item> - <item>"93"</item> - <item>"94"</item> - <item>"95"</item> - <item>"96"</item> - </string-array> - -</resources> diff --git a/core/res/res/values-mcc220/config.xml b/core/res/res/values-mcc220/config.xml deleted file mode 100644 index 7ae82fa91c95..000000000000 --- a/core/res/res/values-mcc220/config.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. --> -<resources> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array name="config_twoDigitNumberPattern"> - <item>"92"</item> - <item>"93"</item> - <item>"94"</item> - <item>"95"</item> - <item>"96"</item> - </string-array> - -</resources> diff --git a/core/res/res/values-mcc310-mnc150/config.xml b/core/res/res/values-mcc310-mnc150/config.xml deleted file mode 100644 index e7d1325ff78a..000000000000 --- a/core/res/res/values-mcc310-mnc150/config.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"0"</item> - <item>"00"</item> - <item>"*0"</item> - <item>"*1"</item> - <item>"*2"</item> - <item>"*3"</item> - <item>"*4"</item> - <item>"*5"</item> - <item>"*6"</item> - <item>"*7"</item> - <item>"*8"</item> - <item>"*9"</item> - <item>"#0"</item> - <item>"#1"</item> - <item>"#2"</item> - <item>"#3"</item> - <item>"#4"</item> - <item>"#5"</item> - <item>"#6"</item> - <item>"#7"</item> - <item>"#8"</item> - <item>"#9"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml index 3fb3f0f7e9ff..53e4193c7f38 100644 --- a/core/res/res/values-mcc310-mnc410/config.xml +++ b/core/res/res/values-mcc310-mnc410/config.xml @@ -23,31 +23,6 @@ <!-- Configure mobile network MTU. Carrier specific value is set here. --> <integer name="config_mobile_mtu">1410</integer> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array name="config_twoDigitNumberPattern"> - <item>"0"</item> - <item>"00"</item> - <item>"*0"</item> - <item>"*1"</item> - <item>"*2"</item> - <item>"*3"</item> - <item>"*4"</item> - <item>"*5"</item> - <item>"*6"</item> - <item>"*7"</item> - <item>"*8"</item> - <item>"*9"</item> - <item>"#0"</item> - <item>"#1"</item> - <item>"#2"</item> - <item>"#3"</item> - <item>"#4"</item> - <item>"#5"</item> - <item>"#6"</item> - <item>"#7"</item> - <item>"#8"</item> - <item>"#9"</item> - </string-array> <!-- Enable 5 bar signal strength icon --> <bool name="config_inflateSignalStrength">true</bool> diff --git a/core/res/res/values-mcc313-mnc100/config.xml b/core/res/res/values-mcc313-mnc100/config.xml deleted file mode 100644 index ccd03f10616a..000000000000 --- a/core/res/res/values-mcc313-mnc100/config.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2019, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"0"</item> - <item>"00"</item> - <item>"*0"</item> - <item>"*1"</item> - <item>"*2"</item> - <item>"*3"</item> - <item>"*4"</item> - <item>"*5"</item> - <item>"*6"</item> - <item>"*7"</item> - <item>"*8"</item> - <item>"*9"</item> - <item>"#0"</item> - <item>"#1"</item> - <item>"#2"</item> - <item>"#3"</item> - <item>"#4"</item> - <item>"#5"</item> - <item>"#6"</item> - <item>"#7"</item> - <item>"#8"</item> - <item>"#9"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc334-mnc050/config.xml b/core/res/res/values-mcc334-mnc050/config.xml index 23678f15fba3..2e8c504f7d8e 100644 --- a/core/res/res/values-mcc334-mnc050/config.xml +++ b/core/res/res/values-mcc334-mnc050/config.xml @@ -31,8 +31,4 @@ <item>9</item> </integer-array> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"#9"</item> - </string-array> </resources> diff --git a/core/res/res/values-mcc334-mnc090/config.xml b/core/res/res/values-mcc334-mnc090/config.xml deleted file mode 100644 index 1632a4239923..000000000000 --- a/core/res/res/values-mcc334-mnc090/config.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2017, 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 my 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. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. --> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"#9"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc704-mnc01/config.xml b/core/res/res/values-mcc704-mnc01/config.xml deleted file mode 100644 index 10b647044c73..000000000000 --- a/core/res/res/values-mcc704-mnc01/config.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2016, 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 my obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<resources> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> - <string-array name="config_twoDigitNumberPattern"> - <item>"*1"</item> - <item>"*5"</item> - <item>"*9"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc708-mnc001/config.xml b/core/res/res/values-mcc708-mnc001/config.xml deleted file mode 100755 index 7b7c48d46b14..000000000000 --- a/core/res/res/values-mcc708-mnc001/config.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2016, 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 my 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. -*/ ---> - -<!-- These resources are around just to allow their values to be customized - for different hardware and product builds. --> -<resources> - <string-array translatable="false" name="config_twoDigitNumberPattern"> - <item>"*1"</item> - <item>"*5"</item> - </string-array> -</resources> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 7b173054aaa3..4054a3abf108 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1631,12 +1631,9 @@ <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дууг санал болгосноос чанга болгож өсгөх үү?\n\nУрт хугацаанд чанга хөгжим сонсох нь таны сонсголыг муутгаж болно."</string> <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Хүртээмжийн товчлолыг ашиглах уу?"</string> <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Товчлол асаалттай үед дууны түвшний хоёр товчлуурыг хамтад нь 3 секунд дарснаар хандалтын онцлогийг эхлүүлнэ."</string> - <!-- no translation found for accessibility_select_shortcut_menu_title (7310194076629867377) --> - <skip /> - <!-- no translation found for accessibility_edit_shortcut_menu_button_title (6096484087245145325) --> - <skip /> - <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (4849108668454490699) --> - <skip /> + <string name="accessibility_select_shortcut_menu_title" msgid="7310194076629867377">"Ашиглахыг хүсэж буй хандалтын аппаа товших"</string> + <string name="accessibility_edit_shortcut_menu_button_title" msgid="6096484087245145325">"Хандалтын товчлуурын тусламжтай ашиглахыг хүсэж буй аппуудаа сонгох"</string> + <string name="accessibility_edit_shortcut_menu_volume_title" msgid="4849108668454490699">"Дууны түвшин тохируулах түлхүүрийн товчлолын тусламжтай ашиглахыг хүсэж буй аппуудаа сонгох"</string> <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Товчлолуудыг засах"</string> <string name="cancel_accessibility_shortcut_menu_button" msgid="1817413122335452474">"Болих"</string> <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Товчлолыг унтраах"</string> @@ -2033,29 +2030,19 @@ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>-г ХЯЗГААРЛАСАН сагс руу орууллаа"</string> <string name="resolver_personal_tab" msgid="2051260504014442073">"Хувийн"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"Ажил"</string> - <!-- no translation found for resolver_personal_tab_accessibility (5739524949153091224) --> - <skip /> - <!-- no translation found for resolver_work_tab_accessibility (4753168230363802734) --> - <skip /> + <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Хувийн харагдах байдал"</string> + <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Ажлын харагдах байдал"</string> <string name="resolver_cant_share_with_work_apps" msgid="7539495559434146897">"Ажлын аппуудтай хуваалцах боломжгүй"</string> <string name="resolver_cant_share_with_personal_apps" msgid="8020581735267157241">"Хувийн аппуудтай хуваалцах боломжгүй"</string> - <!-- no translation found for resolver_cant_share_cross_profile_explanation (5556640604460901386) --> - <skip /> - <!-- no translation found for resolver_cant_access_work_apps (375634344111233790) --> - <skip /> - <!-- no translation found for resolver_cant_access_work_apps_explanation (3958762224516867388) --> - <skip /> - <!-- no translation found for resolver_cant_access_personal_apps (1953215925406474177) --> - <skip /> - <!-- no translation found for resolver_cant_access_personal_apps_explanation (1725572276741281136) --> - <skip /> - <!-- no translation found for resolver_turn_on_work_apps_share (619263911204978175) --> - <skip /> - <!-- no translation found for resolver_turn_on_work_apps_view (3073389230905543680) --> - <skip /> + <string name="resolver_cant_share_cross_profile_explanation" msgid="5556640604460901386">"Таны IT админ хувийн болон ажлын профайлуудын хооронд хуваалцахыг блоклосон"</string> + <string name="resolver_cant_access_work_apps" msgid="375634344111233790">"Ажлын аппуудад хандах боломжгүй байна"</string> + <string name="resolver_cant_access_work_apps_explanation" msgid="3958762224516867388">"Таны IT админ ажлын аппууд дахь хувийн контентыг харахыг танд зөвшөөрөхгүй байна"</string> + <string name="resolver_cant_access_personal_apps" msgid="1953215925406474177">"Хувийн аппуудад хандах боломжгүй байна"</string> + <string name="resolver_cant_access_personal_apps_explanation" msgid="1725572276741281136">"Таны IT админ хувийн аппууд дахь ажлын контентыг харахыг танд зөвшөөрөхгүй байна"</string> + <string name="resolver_turn_on_work_apps_share" msgid="619263911204978175">"Контентыг хуваалцахын тулд ажлын профайлыг асаана уу"</string> + <string name="resolver_turn_on_work_apps_view" msgid="3073389230905543680">"Контентыг харахын тулд ажлын профайлыг асаана уу"</string> <string name="resolver_no_apps_available" msgid="7710339903040989654">"Боломжтой апп алга байна"</string> - <!-- no translation found for resolver_switch_on_work (2873009160846966379) --> - <skip /> + <string name="resolver_switch_on_work" msgid="2873009160846966379">"Асаах"</string> <string name="permlab_accessCallAudio" msgid="1682957511874097664">"Утасны дуудлагын үеэр аудио бичих эсвэл тоглуулах"</string> <string name="permdesc_accessCallAudio" msgid="8448360894684277823">"Энэ аппыг залгагч өгөгдмөл аппликэйшн болгосон үед түүнд утасны дуудлагын үеэр аудио бичих эсвэл тоглуулахыг зөвшөөрдөг."</string> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0b6e65fe9407..617949df865c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2024,7 +2024,10 @@ a keyboard is present. --> <bool name="config_showMenuShortcutsWhenKeyboardPresent">false</bool> - <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> + <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD. + + Note: This config is deprecated, please use carrier config which is + CarrierConfigManager.KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY instead. --> <string-array name="config_twoDigitNumberPattern" translatable="false"> </string-array> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 91186179c6d8..2faa0c9c8036 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -776,7 +776,6 @@ <dimen name="resolver_empty_state_height_with_tabs">268dp</dimen> <dimen name="resolver_max_collapsed_height">192dp</dimen> <dimen name="resolver_max_collapsed_height_with_tabs">248dp</dimen> - <dimen name="resolver_tab_divider_bottom_padding">8dp</dimen> <dimen name="chooser_action_button_icon_size">18dp</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 008c991a24ac..789628d63d1b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1166,6 +1166,11 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] --> <string name="permdesc_systemCamera">This privileged | system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] --> + <string name="permlab_cameraOpenCloseListener">Allow an application or service to receive callbacks about camera devices being opened or closed.</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] --> + <string name="permdesc_cameraOpenCloseListener">This signature app can receive callbacks when any camera device is being opened (by what application package) or closed.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_vibrate">control vibration</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -1212,6 +1217,14 @@ device. This includes information such as call numbers for calls and the state of the calls.</string> + <!-- Title of an application permission. When granted the app is exempt from audio record + restrictions. + [CHAR LIMIT=NONE]--> + <string name="permlab_exemptFromAudioRecordRestrictions">exempt from audio record restrictions</string> + <!-- Description of an application permission. When granted the app is exempt from audio record + restrictions. [CHAR LIMIT=NONE]--> + <string name="permdesc_exemptFromAudioRecordRestrictions">Exempt the app from restrictions to record audio.</string> + <!-- Title of an application permission. When granted the user is giving access to a third party app to continue a call which originated in another app. For example, the user could be in a voice call over their carrier's mobile network, and a third party video @@ -3402,8 +3415,6 @@ <!-- A notification is shown when connected network without internet due to private dns validation failed. This is the notification's message. [CHAR LIMIT=NONE] --> <string name="private_dns_broken_detailed">Private DNS server cannot be accessed</string> - <!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] --> - <string name="captive_portal_logged_in_detailed">Connected</string> <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] --> <string name="network_partial_connectivity"><xliff:g id="network_ssid" example="GoogleGuest">%1$s</xliff:g> has limited connectivity</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9015cc4469ea..8316081dce55 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -688,7 +688,6 @@ <java-symbol type="string" name="capability_title_canControlMagnification" /> <java-symbol type="string" name="capability_desc_canPerformGestures" /> <java-symbol type="string" name="capability_title_canPerformGestures" /> - <java-symbol type="string" name="captive_portal_logged_in_detailed" /> <java-symbol type="string" name="capital_off" /> <java-symbol type="string" name="capital_on" /> <java-symbol type="string" name="cfTemplateForwarded" /> @@ -3910,7 +3909,6 @@ <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="dimen" name="resolver_tab_divider_bottom_padding" /> <!-- Toast message for background started foreground service while-in-use permission restriction feature --> <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" /> diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java index c307e648752d..328429c6f96e 100644 --- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java +++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java @@ -39,6 +39,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Collection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -194,8 +195,8 @@ abstract class AbstractCrossUserContentResolverTest { } @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - if (mExpectedUri.equals(uri) && mExpectedUserId == userId) { + public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) { + if (uris.contains(mExpectedUri) && mExpectedUserId == userId) { mLatch.countDown(); } } diff --git a/core/tests/coretests/src/android/content/ApexContextTest.java b/core/tests/coretests/src/android/content/ApexEnvironmentTest.java index d15c64d0935d..438c5ae1b023 100644 --- a/core/tests/coretests/src/android/content/ApexContextTest.java +++ b/core/tests/coretests/src/android/content/ApexEnvironmentTest.java @@ -28,20 +28,21 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) -public class ApexContextTest { +public class ApexEnvironmentTest { @Test public void dataDirectoryPathsAreAsExpected() { - ApexContext apexContext = ApexContext.getApexContext("my.apex"); + ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment("my.apex"); assertEquals("/data/misc/apexdata/my.apex", - apexContext.getDeviceProtectedDataDir().getAbsolutePath()); + apexEnvironment.getDeviceProtectedDataDir().getAbsolutePath()); assertEquals("/data/misc_de/5/apexdata/my.apex", - apexContext.getDeviceProtectedDataDirForUser(UserHandle.of(5)).getAbsolutePath()); + apexEnvironment + .getDeviceProtectedDataDirForUser(UserHandle.of(5)).getAbsolutePath()); assertEquals("/data/misc_ce/16/apexdata/my.apex", - apexContext.getCredentialProtectedDataDirForUser( + apexEnvironment.getCredentialProtectedDataDirForUser( UserHandle.of(16)).getAbsolutePath()); } } diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java index d98ceaf57dd9..c0325caf1425 100644 --- a/core/tests/coretests/src/android/os/EnvironmentTest.java +++ b/core/tests/coretests/src/android/os/EnvironmentTest.java @@ -23,7 +23,10 @@ import static android.os.Environment.HAS_OTHER; import static android.os.Environment.classifyExternalStorageDirectory; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import android.app.AppOpsManager; import android.content.Context; import androidx.test.InstrumentationRegistry; @@ -40,10 +43,33 @@ import java.io.File; public class EnvironmentTest { private File dir; - private Context getContext() { + private static Context getContext() { return InstrumentationRegistry.getContext(); } + /** + * Sets {@code mode} for the given {@code ops} and the given {@code uid}. + * + * <p>This method drops shell permission identity. + */ + private static void setAppOpsModeForUid(int uid, int mode, String... ops) { + if (ops == null) { + return; + } + InstrumentationRegistry.getInstrumentation() + .getUiAutomation() + .adoptShellPermissionIdentity(); + try { + for (String op : ops) { + getContext().getSystemService(AppOpsManager.class).setUidMode(op, uid, mode); + } + } finally { + InstrumentationRegistry.getInstrumentation() + .getUiAutomation() + .dropShellPermissionIdentity(); + } + } + @Before public void setUp() throws Exception { dir = getContext().getDir("testing", Context.MODE_PRIVATE); @@ -101,4 +127,17 @@ public class EnvironmentTest { Environment.buildPath(dir, "Taxes.pdf").createNewFile(); assertEquals(HAS_OTHER, classifyExternalStorageDirectory(dir)); } + + @Test + public void testIsExternalStorageManager() throws Exception { + assertFalse(Environment.isExternalStorageManager()); + try { + setAppOpsModeForUid(Process.myUid(), AppOpsManager.MODE_ALLOWED, + AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE); + assertTrue(Environment.isExternalStorageManager()); + } finally { + setAppOpsModeForUid(Process.myUid(), AppOpsManager.MODE_DEFAULT, + AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE); + } + } } diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 7737b1a2a776..023fc1736aca 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -49,6 +49,7 @@ import android.os.CancellationSignal; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type; +import android.view.WindowInsetsController.OnControllableInsetsChangedListener; import android.view.WindowManager.BadTokenException; import android.view.WindowManager.LayoutParams; import android.view.animation.LinearInterpolator; @@ -67,6 +68,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mockito; import java.util.concurrent.CountDownLatch; import java.util.function.Supplier; @@ -171,15 +174,24 @@ public class InsetsControllerTest { mController.onControlsChanged(new InsetsSourceControl[] { control }); assertEquals(mLeash, mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash()); + mController.addOnControllableInsetsChangedListener( + ((controller, typeMask) -> assertEquals(statusBars(), typeMask))); } @Test public void testControlsRevoked() { + OnControllableInsetsChangedListener listener + = mock(OnControllableInsetsChangedListener.class); + mController.addOnControllableInsetsChangedListener(listener); InsetsSourceControl control = new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); mController.onControlsChanged(new InsetsSourceControl[] { control }); mController.onControlsChanged(new InsetsSourceControl[0]); assertNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl()); + InOrder inOrder = Mockito.inOrder(listener); + inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0)); + inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(statusBars())); + inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0)); } @Test @@ -206,10 +218,15 @@ public class InsetsControllerTest { public void testFrameDoesntMatchDisplay() { mController.onFrameChanged(new Rect(0, 0, 100, 100)); mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200)); + InsetsSourceControl control = + new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); + mController.onControlsChanged(new InsetsSourceControl[] { control }); WindowInsetsAnimationControlListener controlListener = mock(WindowInsetsAnimationControlListener.class); mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(), controlListener); + mController.addOnControllableInsetsChangedListener( + (controller, typeMask) -> assertEquals(0, typeMask)); verify(controlListener).onCancelled(); verify(controlListener, never()).onReady(any(), anyInt()); } diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java index 9787b7780702..9797178fca6e 100644 --- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java @@ -25,12 +25,14 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.os.CancellationSignal; import android.platform.test.annotations.Presubmit; +import android.view.WindowInsetsController.OnControllableInsetsChangedListener; import android.view.animation.LinearInterpolator; import org.junit.Before; @@ -163,11 +165,43 @@ public class PendingInsetsControllerTest { } @Test + public void testAddOnControllableInsetsChangedListener() { + OnControllableInsetsChangedListener listener = + mock(OnControllableInsetsChangedListener.class); + mPendingInsetsController.addOnControllableInsetsChangedListener(listener); + mPendingInsetsController.replayAndAttach(mReplayedController); + verify(mReplayedController).addOnControllableInsetsChangedListener(eq(listener)); + verify(listener).onControllableInsetsChanged(eq(mPendingInsetsController), eq(0)); + } + + @Test + public void testAddRemoveControllableInsetsChangedListener() { + OnControllableInsetsChangedListener listener = + mock(OnControllableInsetsChangedListener.class); + mPendingInsetsController.addOnControllableInsetsChangedListener(listener); + mPendingInsetsController.removeOnControllableInsetsChangedListener(listener); + mPendingInsetsController.replayAndAttach(mReplayedController); + verify(mReplayedController, never()).addOnControllableInsetsChangedListener(any()); + verify(listener).onControllableInsetsChanged(eq(mPendingInsetsController), eq(0)); + } + + @Test + public void testAddOnControllableInsetsChangedListener_direct() { + mPendingInsetsController.replayAndAttach(mReplayedController); + OnControllableInsetsChangedListener listener = + mock(OnControllableInsetsChangedListener.class); + mPendingInsetsController.addOnControllableInsetsChangedListener(listener); + verify(mReplayedController).addOnControllableInsetsChangedListener(eq(listener)); + } + + @Test public void testReplayTwice() { mPendingInsetsController.show(systemBars()); mPendingInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); mPendingInsetsController.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS); + mPendingInsetsController.addOnControllableInsetsChangedListener( + (controller, typeMask) -> {}); mPendingInsetsController.replayAndAttach(mReplayedController); InsetsController secondController = mock(InsetsController.class); mPendingInsetsController.replayAndAttach(secondController); diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index 0a094c61d4d5..f81964c9cdf9 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -35,10 +35,13 @@ import static org.junit.Assert.assertTrue; import android.app.Activity; import android.app.Instrumentation; +import android.graphics.Rect; import android.text.Layout; +import android.util.ArraySet; import android.util.Log; import android.view.InputDevice; import android.view.MotionEvent; +import android.view.View; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -50,11 +53,13 @@ import com.android.frameworks.coretests.R; import com.google.common.base.Strings; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Set; import java.util.concurrent.atomic.AtomicLong; @RunWith(AndroidJUnit4.class) @@ -70,6 +75,7 @@ public class EditorCursorDragTest { private Instrumentation mInstrumentation; private Activity mActivity; + private Set<MotionEvent> mMotionEvents = new ArraySet<>(); @Before public void before() throws Throwable { @@ -77,6 +83,14 @@ public class EditorCursorDragTest { mActivity = mActivityRule.getActivity(); } + @After + public void after() throws Throwable { + for (MotionEvent event : mMotionEvents) { + event.recycle(); + } + mMotionEvents.clear(); + } + @Test public void testCursorDrag_horizontal_whenTextViewContentsFitOnScreen() throws Throwable { String text = "Hello world!"; @@ -243,45 +257,45 @@ public class EditorCursorDragTest { // Simulate a tap-and-drag gesture. long event1Time = 1001; - MotionEvent event1 = downEvent(event1Time, event1Time, 5f, 10f); + MotionEvent event1 = downEvent(tv, event1Time, event1Time, 5f, 10f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event2Time = 1002; - MotionEvent event2 = moveEvent(event1Time, event2Time, 50f, 10f); + MotionEvent event2 = moveEvent(tv, event1Time, event2Time, 50f, 10f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2)); assertTrue(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event3Time = 1003; - MotionEvent event3 = moveEvent(event1Time, event3Time, 100f, 10f); + MotionEvent event3 = moveEvent(tv, event1Time, event3Time, 100f, 10f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3)); assertTrue(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event4Time = 2004; - MotionEvent event4 = upEvent(event1Time, event4Time, 100f, 10f); + MotionEvent event4 = upEvent(tv, event1Time, event4Time, 100f, 10f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); // Simulate a quick tap after the drag, near the location where the drag ended. long event5Time = 2005; - MotionEvent event5 = downEvent(event5Time, event5Time, 90f, 10f); + MotionEvent event5 = downEvent(tv, event5Time, event5Time, 90f, 10f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event6Time = 2006; - MotionEvent event6 = upEvent(event5Time, event6Time, 90f, 10f); + MotionEvent event6 = upEvent(tv, event5Time, event6Time, 90f, 10f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event6)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); // Simulate another quick tap in the same location; now selection should be triggered. long event7Time = 2007; - MotionEvent event7 = downEvent(event7Time, event7Time, 90f, 10f); + MotionEvent event7 = downEvent(tv, event7Time, event7Time, 90f, 10f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event7)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertTrue(editor.getSelectionController().isCursorBeingModified()); @@ -298,19 +312,19 @@ public class EditorCursorDragTest { // Simulate a mouse click and drag. This should NOT trigger a cursor drag. long event1Time = 1001; - MotionEvent event1 = mouseDownEvent(event1Time, event1Time, 20f, 30f); + MotionEvent event1 = mouseDownEvent(tv, event1Time, event1Time, 20f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event2Time = 1002; - MotionEvent event2 = mouseMoveEvent(event1Time, event2Time, 120f, 30f); + MotionEvent event2 = mouseMoveEvent(tv, event1Time, event2Time, 120f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertTrue(editor.getSelectionController().isCursorBeingModified()); long event3Time = 1003; - MotionEvent event3 = mouseUpEvent(event1Time, event3Time, 120f, 30f); + MotionEvent event3 = mouseUpEvent(tv, event1Time, event3Time, 120f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); @@ -327,25 +341,25 @@ public class EditorCursorDragTest { // Simulate a tap-and-drag gesture. This should trigger a cursor drag. long event1Time = 1001; - MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); + MotionEvent event1 = downEvent(tv, event1Time, event1Time, 20f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event2Time = 1002; - MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f); + MotionEvent event2 = moveEvent(tv, event1Time, event2Time, 21f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event3Time = 1003; - MotionEvent event3 = moveEvent(event1Time, event3Time, 120f, 30f); + MotionEvent event3 = moveEvent(tv, event1Time, event3Time, 120f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3)); assertTrue(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event4Time = 1004; - MotionEvent event4 = upEvent(event1Time, event4Time, 120f, 30f); + MotionEvent event4 = upEvent(tv, event1Time, event4Time, 120f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); @@ -362,31 +376,31 @@ public class EditorCursorDragTest { // Simulate a double-tap followed by a drag. This should trigger a selection drag. long event1Time = 1001; - MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); + MotionEvent event1 = downEvent(tv, event1Time, event1Time, 20f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event2Time = 1002; - MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f); + MotionEvent event2 = upEvent(tv, event1Time, event2Time, 20f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); long event3Time = 1003; - MotionEvent event3 = downEvent(event3Time, event3Time, 20f, 30f); + MotionEvent event3 = downEvent(tv, event3Time, event3Time, 20f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertTrue(editor.getSelectionController().isCursorBeingModified()); long event4Time = 1004; - MotionEvent event4 = moveEvent(event3Time, event4Time, 120f, 30f); + MotionEvent event4 = moveEvent(tv, event3Time, event4Time, 120f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertTrue(editor.getSelectionController().isCursorBeingModified()); long event5Time = 1005; - MotionEvent event5 = upEvent(event3Time, event5Time, 120f, 30f); + MotionEvent event5 = upEvent(tv, event3Time, event5Time, 120f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5)); assertFalse(editor.getInsertionController().isCursorBeingModified()); assertFalse(editor.getSelectionController().isCursorBeingModified()); @@ -403,7 +417,7 @@ public class EditorCursorDragTest { // Simulate a tap. No error should be thrown. long event1Time = 1001; - MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f); + MotionEvent event1 = downEvent(tv, event1Time, event1Time, 20f, 30f); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1)); // Swipe left to right. No error should be thrown. @@ -440,7 +454,8 @@ public class EditorCursorDragTest { public void testCursorDrag_snapToHandle() throws Throwable { String text = "line1: This is the 1st line: A\n" + "line2: This is the 2nd line: B\n" - + "line3: This is the 3rd line: C\n"; + + "line3: This is the 3rd line: C\n" + + "line4: This is the 4th line: D\n"; onView(withId(R.id.textview)).perform(replaceText(text)); onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0)); TextView tv = mActivity.findViewById(R.id.textview); @@ -454,8 +469,8 @@ public class EditorCursorDragTest { // Start dragging along the first line motionEventInfo(text.indexOf("line1"), 1.0f), motionEventInfo(text.indexOf("This is the 1st"), 1.0f), - // Move to the bottom of the third line; cursor should end up on second line - motionEventInfo(text.indexOf("he 3rd"), 0.0f, text.indexOf("he 2nd")), + // Move to the middle of the fourth line; cursor should end up on second line + motionEventInfo(text.indexOf("he 4th"), 0.5f, text.indexOf("he 2nd")), // Move to the middle of the second line; cursor should end up on the first line motionEventInfo(text.indexOf("he 2nd"), 0.5f, text.indexOf("he 1st")) }; @@ -473,39 +488,139 @@ public class EditorCursorDragTest { simulateDrag(tv, events, true); } - private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) { - return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0); + @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" + + "line3: This is the 3rd line: C\n"; + onView(withId(R.id.textview)).perform(replaceText(text)); + TextView tv = mActivity.findViewById(R.id.textview); + Editor editor = tv.getEditorForTesting(); + final int startIndex = text.indexOf("he 2nd"); + Layout layout = tv.getLayout(); + final float cursorStartX = layout.getPrimaryHorizontal(startIndex) + tv.getTotalPaddingLeft(); + final float cursorStartY = layout.getLineTop(1) + tv.getTotalPaddingTop(); + final float dragHandleStartX = 20; + final float dragHandleStartY = 20; + + // Drag the handle from the 2nd line to the 3rd line. + tapAtPoint(tv, cursorStartX, cursorStartY); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex)); + View handleView = editor.getInsertionController().getHandle(); + final int rawYOfHandleDrag = dragDownUntilLineChange( + handleView, dragHandleStartX, dragHandleStartY, tv.getSelectionStart()); + + // Drag the cursor from the 2nd line to the 3rd line. + tapAtPoint(tv, cursorStartX, cursorStartY); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex)); + final int rawYOfCursorDrag = + dragDownUntilLineChange(tv, cursorStartX, cursorStartY, tv.getSelectionStart()); + + // Drag the handle with touch through from the 2nd line to the 3rd line. + tv.getEditorForTesting().setFlagInsertionHandleGesturesEnabled(true); + tapAtPoint(tv, cursorStartX, cursorStartY); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex)); + handleView = editor.getInsertionController().getHandle(); + int rawYOfHandleDragWithTouchThrough = + dragDownUntilLineChange(handleView, dragHandleStartX, dragHandleStartY, tv.getSelectionStart()); + + String msg = String.format( + "rawYOfHandleDrag: %d, rawYOfCursorDrag: %d, rawYOfHandleDragWithTouchThrough: %d", + rawYOfHandleDrag, rawYOfCursorDrag, rawYOfHandleDragWithTouchThrough); + final int max = Math.max( + rawYOfCursorDrag, Math.max(rawYOfHandleDrag, rawYOfHandleDragWithTouchThrough)); + final int min = Math.min( + rawYOfCursorDrag, Math.min(rawYOfHandleDrag, rawYOfHandleDragWithTouchThrough)); + // The drag step is 5 pixels in dragDownUntilLineChange(). + // The difference among the 3 raw Y values should be no bigger than the drag step. + assertWithMessage(msg).that(max - min).isLessThan(6); } - private static MotionEvent upEvent(long downTime, long eventTime, float x, float y) { - return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0); + private void dispatchTouchEvent(View view, MotionEvent event) { + mInstrumentation.runOnMainSync(() -> view.dispatchTouchEvent(event)); } - private static MotionEvent moveEvent(long downTime, long eventTime, float x, float y) { - return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0); + private void tapAtPoint(TextView tv, final float x, final float y) { + long downTime = sTicker.addAndGet(10_000); + dispatchTouchEvent(tv, downEvent(tv, downTime, downTime, x, y)); + dispatchTouchEvent(tv, upEvent(tv, downTime, downTime + 1, x, y)); } - private static MotionEvent mouseDownEvent(long downTime, long eventTime, float x, float y) { - MotionEvent event = downEvent(downTime, eventTime, x, y); - event.setSource(InputDevice.SOURCE_MOUSE); - event.setButtonState(MotionEvent.BUTTON_PRIMARY); - return event; + private int dragDownUntilLineChange(View view, final float startX, final float startY, + final int startOffset) { + TextView tv = mActivity.findViewById(R.id.textview); + final int startLine = tv.getLayout().getLineForOffset(startOffset); + + int rawY = 0; + long downTime = sTicker.addAndGet(10_000); + long eventTime = downTime; + // Move horizontally first to initiate the cursor drag. + dispatchTouchEvent(view, downEvent(view, downTime, eventTime++, startX, startY)); + dispatchTouchEvent(view, moveEvent(view, downTime, eventTime++, startX + 50, startY)); + dispatchTouchEvent(view, moveEvent(view, downTime, eventTime++, startX, startY)); + // Move downwards 5 pixels at a time until a line change occurs. + for (int i = 0; i < 200; i++) { + MotionEvent ev = moveEvent(view, downTime, eventTime++, startX, startY + i * 5); + rawY = (int) ev.getRawY(); + dispatchTouchEvent(view, ev); + if (tv.getLayout().getLineForOffset(tv.getSelectionStart()) > startLine) { + break; + } + } + String msg = String.format("The cursor didn't jump from %d!", startOffset); + assertWithMessage(msg).that( + tv.getLayout().getLineForOffset(tv.getSelectionStart())).isGreaterThan(startLine); + dispatchTouchEvent(view, upEvent(view, downTime, eventTime, startX, startY)); + return rawY; } - private static MotionEvent mouseUpEvent(long downTime, long eventTime, float x, float y) { - MotionEvent event = upEvent(downTime, eventTime, x, y); - event.setSource(InputDevice.SOURCE_MOUSE); - event.setButtonState(0); + private MotionEvent obtainTouchEvent( + View view, int action, long downTime, long eventTime, float x, float y) { + Rect r = new Rect(); + view.getBoundsOnScreen(r); + float rawX = x + r.left; + float rawY = y + r.top; + MotionEvent event = + MotionEvent.obtain(downTime, eventTime, action, rawX, rawY, 0); + view.toLocalMotionEvent(event); + mMotionEvents.add(event); return event; } - private static MotionEvent mouseMoveEvent(long downTime, long eventTime, float x, float y) { - MotionEvent event = moveEvent(downTime, eventTime, x, y); + private MotionEvent obtainMouseEvent( + View view, int action, long downTime, long eventTime, float x, float y) { + MotionEvent event = obtainTouchEvent(view, action, downTime, eventTime, x, y); event.setSource(InputDevice.SOURCE_MOUSE); - event.setButtonState(MotionEvent.BUTTON_PRIMARY); + if (action != MotionEvent.ACTION_UP) { + event.setButtonState(MotionEvent.BUTTON_PRIMARY); + } return event; } + private MotionEvent downEvent(View view, long downTime, long eventTime, float x, float y) { + return obtainTouchEvent(view, MotionEvent.ACTION_DOWN, downTime, eventTime, x, y); + } + + private MotionEvent moveEvent(View view, long downTime, long eventTime, float x, float y) { + return obtainTouchEvent(view, MotionEvent.ACTION_MOVE, downTime, eventTime, x, y); + } + + private MotionEvent upEvent(View view, long downTime, long eventTime, float x, float y) { + return obtainTouchEvent(view, MotionEvent.ACTION_UP, downTime, eventTime, x, y); + } + + private MotionEvent mouseDownEvent(View view, long downTime, long eventTime, float x, float y) { + return obtainMouseEvent(view, MotionEvent.ACTION_DOWN, downTime, eventTime, x, y); + } + + private MotionEvent mouseMoveEvent(View view, long downTime, long eventTime, float x, float y) { + return obtainMouseEvent(view, MotionEvent.ACTION_MOVE, downTime, eventTime, x, y); + } + + private MotionEvent mouseUpEvent(View view, long downTime, long eventTime, float x, float y) { + return obtainMouseEvent(view, MotionEvent.ACTION_UP, downTime, eventTime, x, y); + } + public static MotionEventInfo motionEventInfo(int index, float ratioToLineTop) { return new MotionEventInfo(index, ratioToLineTop, index); } @@ -543,14 +658,15 @@ public class EditorCursorDragTest { float[] downCoords = events[0].getCoordinates(tv); long downEventTime = sTicker.addAndGet(10_000); - MotionEvent downEvent = downEvent(downEventTime, downEventTime, + MotionEvent downEvent = downEvent(tv, downEventTime, downEventTime, downCoords[0], downCoords[1]); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(downEvent)); for (int i = 1; i < events.length; i++) { float[] moveCoords = events[i].getCoordinates(tv); long eventTime = downEventTime + i; - MotionEvent event = moveEvent(downEventTime, eventTime, moveCoords[0], moveCoords[1]); + MotionEvent event = moveEvent(tv, downEventTime, eventTime, moveCoords[0], + moveCoords[1]); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event)); assertCursorPosition(tv, events[i].expectedCursorIndex, runAssertions); } @@ -558,7 +674,7 @@ public class EditorCursorDragTest { MotionEventInfo lastEvent = events[events.length - 1]; float[] upCoords = lastEvent.getCoordinates(tv); long upEventTime = downEventTime + events.length; - MotionEvent upEvent = upEvent(downEventTime, upEventTime, upCoords[0], upCoords[1]); + MotionEvent upEvent = upEvent(tv, downEventTime, upEventTime, upCoords[0], upCoords[1]); mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(upEvent)); } diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 88a6f9e4af4b..a72be25fedb5 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -497,7 +497,7 @@ public class TextViewActivityTest { @Test public void testInsertionHandle_multiLine() { - final String text = "abcd\n" + "efg\n" + "hijk\n"; + final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n"; onView(withId(R.id.textview)).perform(replaceText(text)); onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); @@ -506,12 +506,12 @@ public class TextViewActivityTest { final TextView textView = mActivity.findViewById(R.id.textview); onHandleView(com.android.internal.R.id.insertion_handle) - .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a'))); - onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a"))); - - onHandleView(com.android.internal.R.id.insertion_handle) .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f'))); onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f"))); + + onHandleView(com.android.internal.R.id.insertion_handle) + .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('i'))); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("i"))); } private void enableFlagsForInsertionHandleGestures() { diff --git a/data/etc/com.android.documentsui.xml b/data/etc/com.android.documentsui.xml index b6671db7fa11..1e570ba9ac1c 100644 --- a/data/etc/com.android.documentsui.xml +++ b/data/etc/com.android.documentsui.xml @@ -20,6 +20,7 @@ <permission name="android.permission.INTERACT_ACROSS_USERS"/> <!-- Permissions required for reading and logging compat changes --> <permission name="android.permission.LOG_COMPAT_CHANGE"/> + <permission name="android.permission.MODIFY_QUIET_MODE"/> <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> </privapp-permissions> </permissions> diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index 38e18a941905..72827a956f78 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -63,5 +63,6 @@ <permission name="android.permission.WRITE_SECURE_SETTINGS"/> <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/> <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> + <permission name="android.permission.CAMERA_OPEN_CLOSE_LISTENER" /> </privapp-permissions> </permissions> diff --git a/identity/java/android/security/identity/CredstoreResultData.java b/identity/java/android/security/identity/CredstoreResultData.java index ef7afca6b888..2ef735eec81d 100644 --- a/identity/java/android/security/identity/CredstoreResultData.java +++ b/identity/java/android/security/identity/CredstoreResultData.java @@ -66,7 +66,7 @@ class CredstoreResultData extends ResultData { } @Override - public @NonNull Collection<String> getNamespaceNames() { + public @NonNull Collection<String> getNamespaces() { return Collections.unmodifiableCollection(mData.keySet()); } diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java index 335636cb07ae..725e3d8e429a 100644 --- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java @@ -105,11 +105,11 @@ class CredstoreWritableIdentityCredential extends WritableIdentityCredential { n++; } - Collection<String> namespaceNames = personalizationData.getNamespaceNames(); + Collection<String> namespaces = personalizationData.getNamespaces(); - EntryNamespaceParcel[] ensParcels = new EntryNamespaceParcel[namespaceNames.size()]; + EntryNamespaceParcel[] ensParcels = new EntryNamespaceParcel[namespaces.size()]; n = 0; - for (String namespaceName : namespaceNames) { + for (String namespaceName : namespaces) { PersonalizationData.NamespaceData nsd = personalizationData.getNamespaceData(namespaceName); diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java index bd439199f914..1db2f6357308 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -209,6 +209,11 @@ public abstract class IdentityCredential { * <p>Note that only items referenced in {@code entriesToRequest} are returned - the * {@code requestMessage} parameter is only used to for enforcing reader authentication. * + * <p>The reason for having {@code requestMessage} and {@code entriesToRequest} as separate + * parameters is that the former represents a request from the remote verifier device + * (optionally signed) and this allows the application to filter the request to not include + * data elements which the user has not consented to sharing. + * * @param requestMessage If not {@code null}, must contain CBOR data conforming to * the schema mentioned above. * @param entriesToRequest The entries to request, organized as a map of namespace diff --git a/identity/java/android/security/identity/PersonalizationData.java b/identity/java/android/security/identity/PersonalizationData.java index 44370a1780f8..b34f2505a6a6 100644 --- a/identity/java/android/security/identity/PersonalizationData.java +++ b/identity/java/android/security/identity/PersonalizationData.java @@ -46,7 +46,7 @@ public class PersonalizationData { return Collections.unmodifiableCollection(mProfiles); } - Collection<String> getNamespaceNames() { + Collection<String> getNamespaces() { return Collections.unmodifiableCollection(mNamespaces.keySet()); } @@ -120,7 +120,7 @@ public class PersonalizationData { * @param value The value to add, in CBOR encoding. * @return The builder. */ - public @NonNull Builder setEntry(@NonNull String namespace, @NonNull String name, + public @NonNull Builder putEntry(@NonNull String namespace, @NonNull String name, @NonNull Collection<AccessControlProfileId> accessControlProfileIds, @NonNull byte[] value) { NamespaceData namespaceData = mData.mNamespaces.get(namespace); diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java index 0982c8a4ab31..13552d619e05 100644 --- a/identity/java/android/security/identity/ResultData.java +++ b/identity/java/android/security/identity/ResultData.java @@ -152,7 +152,7 @@ public abstract class ResultData { * @return collection of name of namespaces containing retrieved entries. May be empty if no * data was retrieved. */ - public abstract @NonNull Collection<String> getNamespaceNames(); + public abstract @NonNull Collection<String> getNamespaces(); /** * Get the names of all entries. @@ -196,8 +196,7 @@ public abstract class ResultData { * @param name the name of the entry to get the value for. * @return the status indicating whether the value was retrieved and if not, why. */ - @Status - public abstract int getStatus(@NonNull String namespaceName, @NonNull String name); + public abstract @Status int getStatus(@NonNull String namespaceName, @NonNull String name); /** * Gets the raw CBOR data for the value of an entry. diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 84c07d7d9dff..39900e65cb8a 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -146,12 +146,11 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran } Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc); - bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) && - MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height()); - layer.setForceFilter(!disableFilter); layer.setSize(displayedWidth, displayedHeight); texTransform.copyTo(layer.getTexTransform()); layer.setImage(image); + // Scaling filter is not explicitly set here, because it is done inside copyLayerInfo + // after checking the necessity based on the src/dest rect size and the transformation. if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) { copyResult = CopyResult::Success; } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index e2309178b297..550e41f28f85 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -103,6 +103,16 @@ public class LocationManager { private final Object mLock = new Object(); /** + * For apps targeting Android R and above, {@link #getProvider(String)} will no longer throw any + * security exceptions. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long GET_PROVIDER_SECURITY_EXCEPTIONS = 150935354L; + + /** * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a * specific package. * @@ -1401,6 +1411,22 @@ public class LocationManager { */ public @Nullable LocationProvider getProvider(@NonNull String provider) { Preconditions.checkArgument(provider != null, "invalid null provider"); + + if (!Compatibility.isChangeEnabled(GET_PROVIDER_SECURITY_EXCEPTIONS)) { + if (NETWORK_PROVIDER.equals(provider) || FUSED_PROVIDER.equals(provider)) { + try { + mContext.enforcePermission(ACCESS_FINE_LOCATION, Process.myPid(), + Process.myUid(), null); + } catch (SecurityException e) { + mContext.enforcePermission(ACCESS_COARSE_LOCATION, Process.myPid(), + Process.myUid(), null); + } + } else { + mContext.enforcePermission(ACCESS_FINE_LOCATION, Process.myPid(), Process.myUid(), + null); + } + } + try { ProviderProperties properties = mService.getProviderProperties(provider); if (properties == null) { diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 45b849f747ff..37e2ab53a1a1 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -377,7 +377,9 @@ public class MediaRouter2 { * @see TransferCallback#onTransferred * @see TransferCallback#onTransferFailed */ - public void transferTo(@Nullable MediaRoute2Info route) { + public void transferTo(@NonNull MediaRoute2Info route) { + Objects.requireNonNull(route, "route must not be null"); + List<RoutingController> controllers = getControllers(); RoutingController controller = controllers.get(controllers.size() - 1); @@ -385,19 +387,25 @@ public class MediaRouter2 { } /** + * Stops the current media routing. If the {@link #getSystemController() system controller} + * controls the media routing, this method is a no-op. + */ + public void stop() { + List<RoutingController> controllers = getControllers(); + RoutingController controller = controllers.get(controllers.size() - 1); + + controller.release(); + } + + /** * Transfers the media of a routing controller to the given route. * @param controller a routing controller controlling media routing. - * @param route the route you want to transfer the media to. Pass {@code null} to stop - * routing controlled by the given controller. + * @param route the route you want to transfer the media to. * @hide */ - void transfer(@NonNull RoutingController controller, @Nullable MediaRoute2Info route) { + void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) { Objects.requireNonNull(controller, "controller must not be null"); - - if (route == null) { - controller.release(); - return; - } + Objects.requireNonNull(route, "route must not be null"); // TODO: Check thread-safety if (!mRoutes.containsKey(route.getId())) { @@ -681,7 +689,7 @@ public class MediaRouter2 { if (removed) { matchingController.release(); - notifyControllerReleased(matchingController); + notifyStopped(matchingController); } } @@ -738,16 +746,16 @@ public class MediaRouter2 { } } - private void notifyControllerUpdated(RoutingController controller) { - for (ControllerCallbackRecord record: mControllerCallbackRecords) { - record.mExecutor.execute(() -> record.mCallback.onControllerUpdated(controller)); + private void notifyStopped(RoutingController controller) { + for (TransferCallbackRecord record: mTransferCallbackRecords) { + record.mExecutor.execute( + () -> record.mTransferCallback.onStopped(controller)); } } - private void notifyControllerReleased(RoutingController controller) { - for (TransferCallbackRecord record: mTransferCallbackRecords) { - record.mExecutor.execute( - () -> record.mTransferCallback.onTransferred(controller, null)); + private void notifyControllerUpdated(RoutingController controller) { + for (ControllerCallbackRecord record: mControllerCallbackRecords) { + record.mExecutor.execute(() -> record.mCallback.onControllerUpdated(controller)); } } @@ -788,20 +796,26 @@ public class MediaRouter2 { * This can happen by calling {@link #transferTo(MediaRoute2Info)} or * {@link RoutingController#release()}. * - * @param oldController the previous controller that controlled routing. - * @param newController the new controller to control routing or {@code null} if the - * previous controller is released. + * @param oldController the previous controller that controlled routing + * @param newController the new controller to control routing * @see #transferTo(MediaRoute2Info) */ public void onTransferred(@NonNull RoutingController oldController, - @Nullable RoutingController newController) {} + @NonNull RoutingController newController) {} /** * Called when {@link #transferTo(MediaRoute2Info)} failed. * - * @param requestedRoute the route info which was used for the transfer. + * @param requestedRoute the route info which was used for the transfer */ public void onTransferFailed(@NonNull MediaRoute2Info requestedRoute) {} + + /** + * Called when a media routing stops. It can be stopped by a user or a provider. + * + * @param controller the controller that controlled the stopped media routing. + */ + public void onStopped(@NonNull RoutingController controller) { } } /** @@ -1186,7 +1200,7 @@ public class MediaRouter2 { } if (removed) { - mHandler.post(() -> notifyControllerReleased(RoutingController.this)); + mHandler.post(() -> notifyStopped(RoutingController.this)); } if (stub != null) { diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java index 7932dcb06d88..ea066321f376 100644 --- a/media/java/android/media/tv/tuner/Lnb.java +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -156,7 +156,7 @@ public class Lnb implements AutoCloseable { private long mNativeContext; - Lnb(int id) { + private Lnb(int id) { mId = id; } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index bcbc12b51a73..08a33f1f861c 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -43,7 +43,11 @@ import android.media.tv.tuner.frontend.FrontendStatus; import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType; import android.media.tv.tuner.frontend.OnTuneEventListener; import android.media.tv.tuner.frontend.ScanCallback; +import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerLnbRequest; +import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; @@ -67,6 +71,7 @@ public class Tuner implements AutoCloseable { private static final String TAG = "MediaTvTuner"; private static final boolean DEBUG = false; + private static final int MSG_RESOURCE_LOST = 1; private static final int MSG_ON_FILTER_EVENT = 2; private static final int MSG_ON_FILTER_STATUS = 3; private static final int MSG_ON_LNB_EVENT = 4; @@ -93,6 +98,8 @@ public class Tuner implements AutoCloseable { } private final Context mContext; + private final TunerResourceManager mTunerResourceManager; + private final int mClientId; private List<Integer> mFrontendIds; private Frontend mFrontend; @@ -102,6 +109,7 @@ public class Tuner implements AutoCloseable { private List<Integer> mLnbIds; private Lnb mLnb; + private Integer mLnbId; @Nullable private OnTuneEventListener mOnTuneEventListener; @Nullable @@ -115,6 +123,15 @@ public class Tuner implements AutoCloseable { @Nullable private Executor mOnResourceLostListenerExecutor; + + private final TunerResourceManager.ResourcesReclaimListener mResourceListener = + new TunerResourceManager.ResourcesReclaimListener() { + @Override + public void onReclaimResources() { + mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST)); + } + }; + /** * Constructs a Tuner instance. * @@ -127,6 +144,14 @@ public class Tuner implements AutoCloseable { @TvInputService.PriorityHintUseCaseType int useCase) { nativeSetup(); mContext = context; + mTunerResourceManager = (TunerResourceManager) + context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE); + + int[] clientId = new int[1]; + ResourceClientProfile profile = new ResourceClientProfile(tvInputSessionId, useCase); + mTunerResourceManager.registerClientProfile( + profile, new HandlerExecutor(mHandler), mResourceListener, clientId); + mClientId = clientId[0]; } /** @@ -226,6 +251,7 @@ public class Tuner implements AutoCloseable { private native List<Integer> nativeGetLnbIds(); private native Lnb nativeOpenLnbById(int id); + private native Lnb nativeOpenLnbByName(String name); private native Descrambler nativeOpenDescrambler(); @@ -275,6 +301,14 @@ public class Tuner implements AutoCloseable { } break; } + case MSG_RESOURCE_LOST: { + if (mOnResourceLostListener != null + && mOnResourceLostListenerExecutor != null) { + mOnResourceLostListenerExecutor.execute( + () -> mOnResourceLostListener.onResourceLost(Tuner.this)); + } + break; + } default: // fall through } @@ -698,7 +732,10 @@ public class Tuner implements AutoCloseable { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(cb, "LnbCallback must not be null"); TunerUtils.checkTunerPermission(mContext); - return openLnbByName(null, executor, cb); + if (mLnbId == null && !requestLnb()) { + return null; + } + return nativeOpenLnbById(mLnbId); } /** @@ -714,11 +751,21 @@ public class Tuner implements AutoCloseable { @Nullable public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) { + Objects.requireNonNull(name, "LNB name must not be null"); Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(cb, "LnbCallback must not be null"); TunerUtils.checkTunerPermission(mContext); - // TODO: use resource manager to get LNB ID. - return new Lnb(0); + return nativeOpenLnbByName(name); + } + + private boolean requestLnb() { + int[] lnbId = new int[1]; + TunerLnbRequest request = new TunerLnbRequest(mClientId); + boolean granted = mTunerResourceManager.requestLnb(request, lnbId); + if (granted) { + mLnbId = lnbId[0]; + } + return granted; } /** diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java index 7b2949472396..8a294429b312 100644 --- a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java @@ -18,6 +18,7 @@ package android.media.tv.tuner.filter; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; @@ -33,7 +34,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -public class AlpFilterConfiguration extends FilterConfiguration { +public final class AlpFilterConfiguration extends FilterConfiguration { /** * IPv4 packet type. */ @@ -123,9 +124,10 @@ public class AlpFilterConfiguration extends FilterConfiguration { /** * Builder for {@link AlpFilterConfiguration}. */ - public static class Builder extends FilterConfiguration.Builder<Builder> { + public static final class Builder { private int mPacketType; private int mLengthType; + private Settings mSettings; private Builder() { } @@ -150,16 +152,20 @@ public class AlpFilterConfiguration extends FilterConfiguration { } /** + * Sets filter settings. + */ + @NonNull + public Builder setSettings(@Nullable Settings settings) { + mSettings = settings; + return this; + } + + /** * Builds a {@link AlpFilterConfiguration} object. */ @NonNull public AlpFilterConfiguration build() { return new AlpFilterConfiguration(mSettings, mPacketType, mLengthType); } - - @Override - Builder self() { - return this; - } } } diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 52bdb595050e..4777fe861420 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -225,6 +225,7 @@ public class Filter implements AutoCloseable { mCallback = cb; mExecutor = executor; } + /** @hide */ public FilterCallback getCallback() { return mCallback; diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java index a8c9356b570a..dd7e5fc5328b 100644 --- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java @@ -16,7 +16,6 @@ package android.media.tv.tuner.filter; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -48,26 +47,4 @@ public abstract class FilterConfiguration { public Settings getSettings() { return mSettings; } - - /** - * Builder for {@link FilterConfiguration}. - * - * @param <T> The subclass to be built. - */ - public abstract static class Builder<T extends Builder<T>> { - /* package */ Settings mSettings; - - /* package */ Builder() { - } - - /** - * Sets filter settings. - */ - @NonNull - public T setSettings(@Nullable Settings settings) { - mSettings = settings; - return self(); - } - /* package */ abstract T self(); - } } diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java index a8dbfa5b11ec..04f34108df30 100644 --- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java @@ -17,6 +17,7 @@ package android.media.tv.tuner.filter; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.Size; import android.annotation.SystemApi; @@ -29,7 +30,7 @@ import android.media.tv.tuner.TunerUtils; * @hide */ @SystemApi -public class IpFilterConfiguration extends FilterConfiguration { +public final class IpFilterConfiguration extends FilterConfiguration { private final byte[] mSrcIpAddress; private final byte[] mDstIpAddress; private final int mSrcPort; @@ -104,12 +105,13 @@ public class IpFilterConfiguration extends FilterConfiguration { /** * Builder for {@link IpFilterConfiguration}. */ - public static class Builder extends FilterConfiguration.Builder<Builder> { + public static final class Builder { private byte[] mSrcIpAddress; private byte[] mDstIpAddress; private int mSrcPort; private int mDstPort; private boolean mPassthrough; + private Settings mSettings; private Builder() { } @@ -156,6 +158,15 @@ public class IpFilterConfiguration extends FilterConfiguration { } /** + * Sets filter settings. + */ + @NonNull + public Builder setSettings(@Nullable Settings settings) { + mSettings = settings; + return this; + } + + /** * Builds a {@link IpFilterConfiguration} object. */ @NonNull @@ -169,10 +180,5 @@ public class IpFilterConfiguration extends FilterConfiguration { return new IpFilterConfiguration( mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough); } - - @Override - Builder self() { - return this; - } } } diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java index 060182985dd0..c0453b4f3d2c 100644 --- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java @@ -17,6 +17,7 @@ package android.media.tv.tuner.filter; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; @@ -28,7 +29,7 @@ import android.media.tv.tuner.TunerUtils; * @hide */ @SystemApi -public class MmtpFilterConfiguration extends FilterConfiguration { +public final class MmtpFilterConfiguration extends FilterConfiguration { private final int mMmtpPid; private MmtpFilterConfiguration(Settings settings, int mmtpPid) { @@ -65,8 +66,9 @@ public class MmtpFilterConfiguration extends FilterConfiguration { /** * Builder for {@link IpFilterConfiguration}. */ - public static class Builder extends FilterConfiguration.Builder<Builder> { + public static final class Builder { private int mMmtpPid; + private Settings mSettings; private Builder() { } @@ -81,16 +83,20 @@ public class MmtpFilterConfiguration extends FilterConfiguration { } /** + * Sets filter settings. + */ + @NonNull + public Builder setSettings(@Nullable Settings settings) { + mSettings = settings; + return this; + } + + /** * Builds a {@link IpFilterConfiguration} object. */ @NonNull public MmtpFilterConfiguration build() { return new MmtpFilterConfiguration(mSettings, mMmtpPid); } - - @Override - Builder self() { - return this; - } } } diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java index ac4fc8313460..c5191bfa9c68 100644 --- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java @@ -17,6 +17,7 @@ package android.media.tv.tuner.filter; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; @@ -28,7 +29,7 @@ import android.media.tv.tuner.TunerUtils; * @hide */ @SystemApi -public class TlvFilterConfiguration extends FilterConfiguration { +public final class TlvFilterConfiguration extends FilterConfiguration { /** * IPv4 packet type. */ @@ -108,10 +109,11 @@ public class TlvFilterConfiguration extends FilterConfiguration { /** * Builder for {@link TlvFilterConfiguration}. */ - public static class Builder extends FilterConfiguration.Builder<Builder> { + public static final class Builder { private int mPacketType; private boolean mIsCompressedIpPacket; private boolean mPassthrough; + private Settings mSettings; private Builder() { } @@ -144,6 +146,15 @@ public class TlvFilterConfiguration extends FilterConfiguration { } /** + * Sets filter settings. + */ + @NonNull + public Builder setSettings(@Nullable Settings settings) { + mSettings = settings; + return this; + } + + /** * Builds a {@link TlvFilterConfiguration} object. */ @NonNull @@ -151,10 +162,5 @@ public class TlvFilterConfiguration extends FilterConfiguration { return new TlvFilterConfiguration( mSettings, mPacketType, mIsCompressedIpPacket, mPassthrough); } - - @Override - Builder self() { - return this; - } } } diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java index 6a8b6daa433c..a7140eb2461a 100644 --- a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java @@ -17,6 +17,7 @@ package android.media.tv.tuner.filter; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; @@ -28,7 +29,7 @@ import android.media.tv.tuner.TunerUtils; * @hide */ @SystemApi -public class TsFilterConfiguration extends FilterConfiguration { +public final class TsFilterConfiguration extends FilterConfiguration { private final int mTpid; private TsFilterConfiguration(Settings settings, int tpid) { @@ -63,8 +64,9 @@ public class TsFilterConfiguration extends FilterConfiguration { /** * Builder for {@link TsFilterConfiguration}. */ - public static class Builder extends FilterConfiguration.Builder<Builder> { + public static final class Builder { private int mTpid; + private Settings mSettings; private Builder() { } @@ -81,16 +83,20 @@ public class TsFilterConfiguration extends FilterConfiguration { } /** + * Sets filter settings. + */ + @NonNull + public Builder setSettings(@Nullable Settings settings) { + mSettings = settings; + return this; + } + + /** * Builds a {@link TsFilterConfiguration} object. */ @NonNull public TsFilterConfiguration build() { return new TsFilterConfiguration(mSettings, mTpid); } - - @Override - Builder self() { - return this; - } } } diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 737c95d73e6c..893e51654b68 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -242,13 +242,6 @@ void JMediaCodec::release() { }); } -void JMediaCodec::releaseAsync() { - if (mCodec != NULL) { - mCodec->releaseAsync(); - } - mInitStatus = NO_INIT; -} - JMediaCodec::~JMediaCodec() { if (mLooper != NULL) { /* MediaCodec and looper should have been released explicitly already @@ -1131,10 +1124,7 @@ static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) { } static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { - sp<JMediaCodec> codec = getMediaCodec(env, thiz); - if (codec != NULL) { - codec->releaseAsync(); - } + setMediaCodec(env, thiz, NULL); } static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, const char *msg) { @@ -2807,7 +2797,7 @@ static void android_media_MediaCodec_native_setup( static void android_media_MediaCodec_native_finalize( JNIEnv *env, jobject thiz) { - setMediaCodec(env, thiz, NULL); + android_media_MediaCodec_release(env, thiz); } // MediaCodec.LinearBlock diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 400ce1bc98e7..8899fee7a73d 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -61,7 +61,6 @@ struct JMediaCodec : public AHandler { void registerSelf(); void release(); - void releaseAsync(); status_t enableOnFrameRenderedListener(jboolean enable); diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 273c77684635..679f18a6fbec 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -34,19 +34,28 @@ using ::android::hardware::Void; using ::android::hardware::hidl_bitfield; using ::android::hardware::hidl_vec; +using ::android::hardware::tv::tuner::V1_0::AudioExtraMetaData; +using ::android::hardware::tv::tuner::V1_0::Constant; using ::android::hardware::tv::tuner::V1_0::DataFormat; using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType; using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterIpPayloadEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMediaEvent; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterMmtpRecordEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionBits; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterTemiEvent; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterTsRecordEvent; using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress; using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType; @@ -136,6 +145,7 @@ struct fields_t { jmethodID dvrInitID; jmethodID onFrontendEventID; jmethodID onFilterStatusID; + jmethodID onFilterEventID; jmethodID lnbInitID; jmethodID onLnbEventID; jmethodID descramblerInitID; @@ -248,48 +258,287 @@ jobject FilterCallback::handleToLinearBlock(const native_handle_t* handle, uint3 return linearBlock; } -jobject FilterCallback::getMediaEvent(const DemuxFilterEvent::Event& event) { +jobjectArray FilterCallback::getSectionEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); - jclass clazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent"); - jmethodID eventInit = env->GetMethodID(clazz, + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIII)V"); + + for (int i = 0; i < events.size(); i++) { + auto event = events[i]; + DemuxFilterSectionEvent sectionEvent = event.section(); + + jint tableId = static_cast<jint>(sectionEvent.tableId); + jint version = static_cast<jint>(sectionEvent.version); + jint sectionNum = static_cast<jint>(sectionEvent.sectionNum); + jint dataLength = static_cast<jint>(sectionEvent.dataLength); + + jobject obj = + env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength); + env->SetObjectArrayElement(arr, i, obj); + } + return arr; +} + +jobjectArray FilterCallback::getMediaEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IZJJJLandroid/media/MediaCodec$LinearBlock;" "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V"); - DemuxFilterMediaEvent mediaEvent = event.media(); - uint32_t dataLength = mediaEvent.dataLength; - const native_handle_t* h = mediaEvent.avMemory.getNativeHandle(); - jobject block = handleToLinearBlock(h, dataLength); - // TODO: handle other fields + for (int i = 0; i < events.size(); i++) { + auto event = events[i]; + DemuxFilterMediaEvent mediaEvent = event.media(); + + jobject audioDescriptor = NULL; + if (mediaEvent.extraMetaData.getDiscriminator() + == DemuxFilterMediaEvent::ExtraMetaData::hidl_discriminator::audio) { + jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor"); + jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V"); + + AudioExtraMetaData ad = mediaEvent.extraMetaData.audio(); + jbyte adFade = static_cast<jbyte>(ad.adFade); + jbyte adPan = static_cast<jbyte>(ad.adPan); + jchar versionTextTag = static_cast<jchar>(ad.versionTextTag); + jbyte adGainCenter = static_cast<jbyte>(ad.adGainCenter); + jbyte adGainFront = static_cast<jbyte>(ad.adGainFront); + jbyte adGainSurround = static_cast<jbyte>(ad.adGainSurround); + + audioDescriptor = + env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag, adGainCenter, + adGainFront, adGainSurround); + } + + jlong dataLength = static_cast<jlong>(mediaEvent.dataLength); + const native_handle_t* h = NULL; + jobject block = NULL; + if (mediaEvent.avMemory != NULL) { + h = mediaEvent.avMemory.getNativeHandle(); + block = handleToLinearBlock(h, dataLength); + } + + jint streamId = static_cast<jint>(mediaEvent.streamId); + jboolean isPtsPresent = static_cast<jboolean>(mediaEvent.isPtsPresent); + jlong pts = static_cast<jlong>(mediaEvent.pts); + jlong offset = static_cast<jlong>(mediaEvent.offset); + jboolean isSecureMemory = static_cast<jboolean>(mediaEvent.isSecureMemory); + jlong avDataId = static_cast<jlong>(mediaEvent.avDataId); + jint mpuSequenceNumber = static_cast<jint>(mediaEvent.mpuSequenceNumber); + jboolean isPesPrivateData = static_cast<jboolean>(mediaEvent.isPesPrivateData); + + jobject obj = + env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength, + offset, block, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData, + audioDescriptor); + env->SetObjectArrayElement(arr, i, obj); + } + return arr; +} + +jobjectArray FilterCallback::getPesEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/PesEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(III)V"); + + for (int i = 0; i < events.size(); i++) { + auto event = events[i]; + DemuxFilterPesEvent pesEvent = event.pes(); + + jint streamId = static_cast<jint>(pesEvent.streamId); + jint dataLength = static_cast<jint>(pesEvent.dataLength); + jint mpuSequenceNumber = static_cast<jint>(pesEvent.mpuSequenceNumber); + + jobject obj = + env->NewObject(eventClazz, eventInit, streamId, dataLength, mpuSequenceNumber); + env->SetObjectArrayElement(arr, i, obj); + } + return arr; +} + +jobjectArray FilterCallback::getTsRecordEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJ)V"); + + for (int i = 0; i < events.size(); i++) { + auto event = events[i]; + DemuxFilterTsRecordEvent tsRecordEvent = event.tsRecord(); + DemuxPid pid = tsRecordEvent.pid; + + jint jpid = static_cast<jint>(Constant::INVALID_TS_PID); + + if (pid.getDiscriminator() == DemuxPid::hidl_discriminator::tPid) { + jpid = static_cast<jint>(pid.tPid()); + } else if (pid.getDiscriminator() == DemuxPid::hidl_discriminator::mmtpPid) { + jpid = static_cast<jint>(pid.mmtpPid()); + } + + jint sc = 0; + + if (tsRecordEvent.scIndexMask.getDiscriminator() + == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::sc) { + sc = static_cast<jint>(tsRecordEvent.scIndexMask.sc()); + } else if (tsRecordEvent.scIndexMask.getDiscriminator() + == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::scHevc) { + sc = static_cast<jint>(tsRecordEvent.scIndexMask.scHevc()); + } + + jint ts = static_cast<jint>(tsRecordEvent.tsIndexMask); + + jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber); + + jobject obj = + env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber); + env->SetObjectArrayElement(arr, i, obj); + } + return arr; +} + +jobjectArray FilterCallback::getMmtpRecordEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJ)V"); - return env->NewObject(clazz, eventInit, (jint) 0, (jboolean) 0, (jlong) 0, (jlong) 0, (jlong) 0, - block, (jboolean) 0, (jlong) 0, (jint) 0, (jboolean) 0, NULL); + for (int i = 0; i < events.size(); i++) { + auto event = events[i]; + DemuxFilterMmtpRecordEvent mmtpRecordEvent = event.mmtpRecord(); + + jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask); + jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber); + + jobject obj = + env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber); + env->SetObjectArrayElement(arr, i, obj); + } + return arr; +} + +jobjectArray FilterCallback::getDownloadEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIII)V"); + + for (int i = 0; i < events.size(); i++) { + auto event = events[i]; + DemuxFilterDownloadEvent downloadEvent = event.download(); + + jint itemId = static_cast<jint>(downloadEvent.itemId); + jint mpuSequenceNumber = static_cast<jint>(downloadEvent.mpuSequenceNumber); + jint itemFragmentIndex = static_cast<jint>(downloadEvent.itemFragmentIndex); + jint lastItemFragmentIndex = static_cast<jint>(downloadEvent.lastItemFragmentIndex); + jint dataLength = static_cast<jint>(downloadEvent.dataLength); + + jobject obj = + env->NewObject(eventClazz, eventInit, itemId, mpuSequenceNumber, itemFragmentIndex, + lastItemFragmentIndex, dataLength); + env->SetObjectArrayElement(arr, i, obj); + } + return arr; +} + +jobjectArray FilterCallback::getIpPayloadEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V"); + + for (int i = 0; i < events.size(); i++) { + auto event = events[i]; + DemuxFilterIpPayloadEvent ipPayloadEvent = event.ipPayload(); + jint dataLength = static_cast<jint>(ipPayloadEvent.dataLength); + jobject obj = env->NewObject(eventClazz, eventInit, dataLength); + env->SetObjectArrayElement(arr, i, obj); + } + return arr; +} + +jobjectArray FilterCallback::getTemiEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TemiEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(JB[B)V"); + + for (int i = 0; i < events.size(); i++) { + auto event = events[i]; + DemuxFilterTemiEvent temiEvent = event.temi(); + jlong pts = static_cast<jlong>(temiEvent.pts); + jbyte descrTag = static_cast<jbyte>(temiEvent.descrTag); + std::vector<uint8_t> descrData = temiEvent.descrData; + + jbyteArray array = env->NewByteArray(descrData.size()); + env->SetByteArrayRegion( + array, 0, descrData.size(), reinterpret_cast<jbyte*>(&descrData[0])); + + jobject obj = env->NewObject(eventClazz, eventInit, pts, descrTag, array); + env->SetObjectArrayElement(arr, i, obj); + } + return arr; } Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) { ALOGD("FilterCallback::onFilterEvent"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - jclass clazz = env->FindClass("android/media/tv/tuner/filter/Filter"); std::vector<DemuxFilterEvent::Event> events = filterEvent.events; jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent"); jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL); - for (int i = 0; i < events.size(); i++) { - auto event = events[i]; - if (event.getDiscriminator() == DemuxFilterEvent::Event::hidl_discriminator::media) { - env->SetObjectArrayElement(array, i, getMediaEvent(event)); + if (!events.empty()) { + auto event = events[0]; + switch (event.getDiscriminator()) { + case DemuxFilterEvent::Event::hidl_discriminator::media: { + array = getMediaEvent(array, events); + break; + } + case DemuxFilterEvent::Event::hidl_discriminator::section: { + array = getSectionEvent(array, events); + break; + } + case DemuxFilterEvent::Event::hidl_discriminator::pes: { + array = getPesEvent(array, events); + break; + } + case DemuxFilterEvent::Event::hidl_discriminator::tsRecord: { + array = getTsRecordEvent(array, events); + break; + } + case DemuxFilterEvent::Event::hidl_discriminator::mmtpRecord: { + array = getMmtpRecordEvent(array, events); + break; + } + case DemuxFilterEvent::Event::hidl_discriminator::download: { + array = getDownloadEvent(array, events); + break; + } + case DemuxFilterEvent::Event::hidl_discriminator::ipPayload: { + array = getIpPayloadEvent(array, events); + break; + } + case DemuxFilterEvent::Event::hidl_discriminator::temi: { + array = getTemiEvent(array, events); + break; + } + default: { + break; + } } } env->CallVoidMethod( mFilter, - env->GetMethodID(clazz, "onFilterEvent", - "([Landroid/media/tv/tuner/filter/FilterEvent;)V"), + gFields.onFilterEventID, array); return Void(); } + Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) { ALOGD("FilterCallback::onFilterStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -308,9 +557,16 @@ void FilterCallback::setFilter(const jobject filter) { /////////////// Filter /////////////////////// -Filter::Filter(sp<IFilter> sp, jweak obj) : mFilterSp(sp), mFilterObj(obj) {} +Filter::Filter(sp<IFilter> sp, jobject obj) : mFilterSp(sp) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + mFilterObj = env->NewWeakGlobalRef(obj); +} Filter::~Filter() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mFilterObj); + mFilterObj = NULL; EventFlag::deleteEventFlag(&mFilterMQEventFlag); } @@ -816,6 +1072,37 @@ jobject JTuner::openLnbById(int id) { return lnbObj; } +jobject JTuner::openLnbByName(jstring name) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + std::string lnbName(env->GetStringUTFChars(name, nullptr)); + sp<ILnb> iLnbSp; + Result res; + LnbId id; + mTuner->openLnbByName(lnbName, [&](Result r, LnbId lnbId, const sp<ILnb>& lnb) { + res = r; + iLnbSp = lnb; + id = lnbId; + }); + if (res != Result::SUCCESS || iLnbSp == nullptr) { + ALOGE("Failed to open lnb"); + return NULL; + } + mLnb = iLnbSp; + sp<LnbCallback> lnbCb = new LnbCallback(mObject, id); + mLnb->setCallback(lnbCb); + + jobject lnbObj = env->NewObject( + env->FindClass("android/media/tv/tuner/Lnb"), + gFields.lnbInitID, + id); + + sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj); + lnbSp->incStrong(lnbObj); + env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get()); + + return lnbObj; +} + int JTuner::tune(const FrontendSettings& settings) { if (mFe == NULL) { ALOGE("frontend is not initialized"); @@ -1616,8 +1903,7 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb"); gFields.lnbContext = env->GetFieldID(lnbClazz, "mNativeContext", "J"); - gFields.lnbInitID = - env->GetMethodID(lnbClazz, "<init>", "(I)V"); + gFields.lnbInitID = env->GetMethodID(lnbClazz, "<init>", "(I)V"); jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter"); gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J"); @@ -1625,6 +1911,9 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { env->GetMethodID(filterClazz, "<init>", "(I)V"); gFields.onFilterStatusID = env->GetMethodID(filterClazz, "onFilterStatus", "(I)V"); + gFields.onFilterEventID = + env->GetMethodID(filterClazz, "onFilterEvent", + "([Landroid/media/tv/tuner/filter/FilterEvent;)V"); jclass timeFilterClazz = env->FindClass("android/media/tv/tuner/filter/TimeFilter"); gFields.timeFilterContext = env->GetFieldID(timeFilterClazz, "mNativeContext", "J"); @@ -1737,6 +2026,12 @@ static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz, return tuner->openLnbById(id); } +static jobject android_media_tv_Tuner_open_lnb_by_name(JNIEnv *env, jobject thiz, jstring name) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->openLnbByName(name); +} + + static jobject android_media_tv_Tuner_open_filter( JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) { sp<JTuner> tuner = getTuner(env, thiz); @@ -2629,6 +2924,8 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_get_lnb_ids }, { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Lnb;", (void *)android_media_tv_Tuner_open_lnb_by_id }, + { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;", + (void *)android_media_tv_Tuner_open_lnb_by_name }, { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Descrambler;", (void *)android_media_tv_Tuner_open_descrambler }, { "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;", diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index fec4cd8d5002..3b8682f0a61b 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -114,7 +114,22 @@ struct FilterCallback : public IFilterCallback { jobject handleToLinearBlock(const native_handle_t* handle, uint32_t size); private: jweak mFilter; - jobject getMediaEvent(const DemuxFilterEvent::Event& event); + jobjectArray getSectionEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); + jobjectArray getMediaEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); + jobjectArray getPesEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); + jobjectArray getTsRecordEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); + jobjectArray getMmtpRecordEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); + jobjectArray getDownloadEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); + jobjectArray getIpPayloadEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); + jobjectArray getTemiEvent( + jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); }; struct FrontendCallback : public IFrontendCallback { @@ -129,7 +144,7 @@ struct FrontendCallback : public IFrontendCallback { }; struct Filter : public RefBase { - Filter(sp<IFilter> sp, jweak obj); + Filter(sp<IFilter> sp, jobject obj); ~Filter(); int close(); sp<IFilter> getIFilter(); @@ -165,6 +180,7 @@ struct JTuner : public RefBase { int setLna(bool enable); jobject getLnbIds(); jobject openLnbById(int id); + jobject openLnbByName(jstring name); jobject openFilter(DemuxFilterType type, int bufferSize); jobject openTimeFilter(); jobject openDescrambler(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index c529952843a1..6354ccd7b3b0 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -320,6 +320,15 @@ public class CameraBinderTest extends AndroidTestCase { public void onCameraAccessPrioritiesChanged() { Log.v(TAG, "Camera access permission change"); } + @Override + public void onCameraOpened(String cameraId, String clientPackageName) { + Log.v(TAG, String.format("Camera %s is opened by client package %s", + cameraId, clientPackageName)); + } + @Override + public void onCameraClosed(String cameraId) { + Log.v(TAG, String.format("Camera %s is closed", cameraId)); + } } /** diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index 22c3acf6f1eb..140c075552c7 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -24,6 +24,7 @@ import android.content.Context; import com.android.keyguard.KeyguardViewController; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarDeviceProvisionedControllerImpl; +import com.android.systemui.car.CarNotificationInterruptionStateProvider; import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; @@ -40,6 +41,7 @@ import com.android.systemui.statusbar.car.CarShadeControllerImpl; import com.android.systemui.statusbar.car.CarStatusBar; import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; @@ -47,6 +49,8 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryControllerImpl; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -63,6 +67,10 @@ import dagger.Provides; @Module(includes = {DividerModule.class}) abstract class CarSystemUIModule { + @Binds + abstract NotificationInterruptionStateProvider bindNotificationInterruptionStateProvider( + CarNotificationInterruptionStateProvider notificationInterruptionStateProvider); + @Singleton @Provides @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) @@ -100,6 +108,11 @@ abstract class CarSystemUIModule { NotificationLockscreenUserManagerImpl notificationLockscreenUserManager); @Binds + @Singleton + public abstract BatteryController provideBatteryController( + BatteryControllerImpl controllerImpl); + + @Binds abstract DockManager bindDockManager(DockManagerImpl dockManager); @Binds diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java new file mode 100644 index 000000000000..447e579ece42 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 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.car; + +import android.content.Context; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.BatteryController; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */ +@Singleton +public class CarNotificationInterruptionStateProvider extends + NotificationInterruptionStateProvider { + + @Inject + public CarNotificationInterruptionStateProvider(Context context, + NotificationFilter filter, + StatusBarStateController stateController, + BatteryController batteryController) { + super(context, filter, stateController, batteryController); + } + + @Override + public boolean shouldHeadsUp(NotificationEntry entry) { + // Because space is usually constrained in the auto use-case, there should not be a + // pinned notification when the shade has been expanded. Ensure this by not pinning any + // notification if the shade is already opened. + if (!getPresenter().isPresenterFullyCollapsed()) { + return false; + } + + return super.shouldHeadsUp(entry); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index de768cbf0877..b2e21045f2a9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -95,14 +95,13 @@ import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.init.NotificationsController; -import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -250,9 +249,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, - NotificationInterruptStateProvider notificationInterruptStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, + NotificationAlertingManager notificationAlertingManager, DisplayMetrics displayMetrics, MetricsLogger metricsLogger, @UiBackground Executor uiBgExecutor, @@ -335,9 +335,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt remoteInputQuickSettingsDisabler, notificationGutsManager, notificationLogger, - notificationInterruptStateProvider, + notificationInterruptionStateProvider, notificationViewHierarchyManager, keyguardViewMediator, + notificationAlertingManager, displayMetrics, metricsLogger, uiBgExecutor, @@ -487,22 +488,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt .isCurrentUserSetupInProgress(); } }); - - mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() { - @Override - public String getName() { - return TAG; - } - - @Override - public boolean suppressInterruptions(NotificationEntry entry) { - // Because space is usually constrained in the auto use-case, there should not be a - // pinned notification when the shade has been expanded. - // Ensure this by not allowing any interruptions (ie: pinning any notifications) if - // the shade is already opened. - return !getPresenter().isPresenterFullyCollapsed(); - } - }); } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index 9a535844d9d8..4754118e7a64 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -62,12 +62,13 @@ import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule; +import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; -import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationRowModule; @@ -145,9 +146,10 @@ public class CarStatusBarModule { RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, - NotificationInterruptStateProvider notificationInterruptionStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, + NotificationAlertingManager notificationAlertingManager, DisplayMetrics displayMetrics, MetricsLogger metricsLogger, @UiBackground Executor uiBgExecutor, @@ -232,6 +234,7 @@ public class CarStatusBarModule { notificationInterruptionStateProvider, notificationViewHierarchyManager, keyguardViewMediator, + notificationAlertingManager, displayMetrics, metricsLogger, uiBgExecutor, diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 3f42ad40eb8e..4ea104705da0 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -2,6 +2,30 @@ android_library { name: "SettingsLib", + defaults: [ + "SettingsLibDependenciesWithoutWifiTracker", + ], + + // TODO(b/149540986): revert this change. + static_libs: [ + // All other dependent components should be put in + // "SettingsLibDependenciesWithoutWifiTracker". + "WifiTrackerLib", + ], + + // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES + // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common + + resource_dirs: ["res"], + + srcs: ["src/**/*.java", "src/**/*.kt"], + + min_sdk_version: "21", + +} + +java_defaults { + name: "SettingsLibDependenciesWithoutWifiTracker", static_libs: [ "androidx.annotation_annotation", "androidx.legacy_legacy-support-v4", @@ -25,20 +49,9 @@ android_library { "SettingsLibProgressBar", "SettingsLibAdaptiveIcon", "SettingsLibRadioButtonPreference", - "WifiTrackerLib", "SettingsLibDisplayDensityUtils", "SettingsLibSchedulesProvider", ], - - // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES - // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common - - resource_dirs: ["res"], - - srcs: ["src/**/*.java", "src/**/*.kt"], - - min_sdk_version: "21", - } // NOTE: Keep this module in sync with ./common.mk diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 7e78a78852a8..eb0ddfb492cb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -24,6 +24,7 @@ import static android.media.MediaRoute2Info.TYPE_REMOTE_TV; import static android.media.MediaRoute2Info.TYPE_UNKNOWN; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; +import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; import android.app.Notification; import android.bluetooth.BluetoothAdapter; @@ -50,6 +51,7 @@ import java.util.concurrent.Executors; public class InfoMediaManager extends MediaManager { private static final String TAG = "InfoMediaManager"; + private static final boolean DEBUG = false; @VisibleForTesting final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback(); @@ -339,6 +341,9 @@ public class InfoMediaManager extends MediaManager { private void buildAllRoutes() { for (MediaRoute2Info route : mRouterManager.getAllRoutes()) { + if (DEBUG) { + Log.d(TAG, "buildAllRoutes() route : " + route.getName()); + } if (route.isSystemRoute()) { addMediaDevice(route); } @@ -347,6 +352,9 @@ public class InfoMediaManager extends MediaManager { private void buildAvailableRoutes() { for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) { + if (DEBUG) { + Log.d(TAG, "buildAvailableRoutes() route : " + route.getName()); + } addMediaDevice(route); } } @@ -363,7 +371,8 @@ public class InfoMediaManager extends MediaManager { mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route, mPackageName); if (!TextUtils.isEmpty(mPackageName) - && TextUtils.equals(route.getClientPackageName(), mPackageName)) { + && TextUtils.equals(route.getClientPackageName(), mPackageName) + && mCurrentConnectedDevice == null) { mCurrentConnectedDevice = mediaDevice; } break; @@ -409,12 +418,41 @@ public class InfoMediaManager extends MediaManager { @Override public void onRoutesChanged(List<MediaRoute2Info> routes) { - refreshDevices(); + mMediaDevices.clear(); + mCurrentConnectedDevice = null; + if (TextUtils.isEmpty(mPackageName)) { + buildAllRoutes(); + } else { + buildAvailableRoutes(); + } + + final String id = mCurrentConnectedDevice != null + ? mCurrentConnectedDevice.getId() + : null; + dispatchConnectedDeviceChanged(id); } @Override public void onRoutesRemoved(List<MediaRoute2Info> routes) { refreshDevices(); } + + @Override + public void onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession) { + if (DEBUG) { + Log.d(TAG, "onTransferred() oldSession : " + oldSession.getName() + + ", newSession : " + newSession.getName()); + } + } + + @Override + public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) { + dispatchOnRequestFailed(REASON_UNKNOWN_ERROR); + } + + @Override + public void onRequestFailed(int reason) { + dispatchOnRequestFailed(reason); + } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index adb3c1170f8f..90f55dd6f38d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -142,20 +142,11 @@ public class LocalMediaManager implements BluetoothCallback { mCurrentConnectedDevice.disconnect(); } - boolean isConnected = false; if (TextUtils.isEmpty(mPackageName)) { - isConnected = mInfoMediaManager.connectDeviceWithoutPackageName(device); + mInfoMediaManager.connectDeviceWithoutPackageName(device); } else { - isConnected = device.connect(); + device.connect(); } - if (isConnected) { - mCurrentConnectedDevice = device; - } - - final int state = isConnected - ? MediaDeviceState.STATE_CONNECTED - : MediaDeviceState.STATE_DISCONNECTED; - dispatchSelectedDeviceStateChanged(device, state); } void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) { @@ -186,6 +177,12 @@ public class LocalMediaManager implements BluetoothCallback { } } + void dispatchOnRequestFailed(int reason) { + for (DeviceCallback callback : getCallbacks()) { + callback.onRequestFailed(reason); + } + } + /** * Stop scan MediaDevice */ @@ -337,7 +334,7 @@ public class LocalMediaManager implements BluetoothCallback { MediaDevice phoneMediaDevice = null; for (MediaDevice device : mMediaDevices) { if (device instanceof BluetoothMediaDevice) { - if (isConnected(((BluetoothMediaDevice) device).getCachedDevice())) { + if (isActiveDevice(((BluetoothMediaDevice) device).getCachedDevice())) { return device; } } else if (device instanceof PhoneMediaDevice) { @@ -347,7 +344,7 @@ public class LocalMediaManager implements BluetoothCallback { return mMediaDevices.contains(phoneMediaDevice) ? phoneMediaDevice : null; } - private boolean isConnected(CachedBluetoothDevice device) { + private boolean isActiveDevice(CachedBluetoothDevice device) { return device.isActiveDevice(BluetoothProfile.A2DP) || device.isActiveDevice(BluetoothProfile.HEARING_AID); } @@ -423,20 +420,28 @@ public class LocalMediaManager implements BluetoothCallback { @Override public void onConnectedDeviceChanged(String id) { - final MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id); + MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id); + connectDevice = connectDevice != null + ? connectDevice : updateCurrentConnectedDevice(); if (connectDevice == mCurrentConnectedDevice) { Log.d(TAG, "onConnectedDeviceChanged() this device all ready connected!"); return; } mCurrentConnectedDevice = connectDevice; - dispatchDeviceAttributesChanged(); + dispatchSelectedDeviceStateChanged(mCurrentConnectedDevice, + MediaDeviceState.STATE_CONNECTED); } @Override public void onDeviceAttributesChanged() { dispatchDeviceAttributesChanged(); } + + @Override + public void onRequestFailed(int reason) { + dispatchOnRequestFailed(reason); + } } @@ -467,6 +472,18 @@ public class LocalMediaManager implements BluetoothCallback { * Callback for notifying the device attributes is changed. */ default void onDeviceAttributesChanged() {}; + + /** + * Callback for notifying that transferring is failed. + * + * @param reason the reason that the request has failed. Can be one of followings: + * {@link android.media.MediaRoute2ProviderService#REASON_UNKNOWN_ERROR}, + * {@link android.media.MediaRoute2ProviderService#REASON_REJECTED}, + * {@link android.media.MediaRoute2ProviderService#REASON_NETWORK_ERROR}, + * {@link android.media.MediaRoute2ProviderService#REASON_ROUTE_NOT_AVAILABLE}, + * {@link android.media.MediaRoute2ProviderService#REASON_INVALID_COMMAND}, + */ + default void onRequestFailed(int reason){}; } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java index 73551f60c462..e8cbab8197b2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java @@ -110,6 +110,12 @@ public abstract class MediaManager { } } + protected void dispatchOnRequestFailed(int reason) { + for (MediaDeviceCallback callback : getCallbacks()) { + callback.onRequestFailed(reason); + } + } + private Collection<MediaDeviceCallback> getCallbacks() { return new CopyOnWriteArrayList<>(mCallbacks); } @@ -158,5 +164,17 @@ public abstract class MediaManager { * (e.g: device name, connection state, subtitle) is changed. */ void onDeviceAttributesChanged(); + + /** + * Callback for notifying that transferring is failed. + * + * @param reason the reason that the request has failed. Can be one of followings: + * {@link android.media.MediaRoute2ProviderService#REASON_UNKNOWN_ERROR}, + * {@link android.media.MediaRoute2ProviderService#REASON_REJECTED}, + * {@link android.media.MediaRoute2ProviderService#REASON_NETWORK_ERROR}, + * {@link android.media.MediaRoute2ProviderService#REASON_ROUTE_NOT_AVAILABLE}, + * {@link android.media.MediaRoute2ProviderService#REASON_INVALID_COMMAND}, + */ + void onRequestFailed(int reason); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index 7cd0a7ce0437..7f93f697152c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -16,6 +16,9 @@ package com.android.settingslib.media; +import static android.media.MediaRoute2ProviderService.REASON_NETWORK_ERROR; +import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; @@ -54,6 +57,8 @@ public class InfoMediaManagerTest { private MediaRouter2Manager mRouterManager; @Mock private LocalBluetoothManager mLocalBluetoothManager; + @Mock + private MediaManager.MediaDeviceCallback mCallback; private InfoMediaManager mInfoMediaManager; private Context mContext; @@ -144,6 +149,8 @@ public class InfoMediaManagerTest { @Test public void onRoutesChanged_getAvailableRoutes_shouldAddMediaDevice() { final MediaRoute2Info info = mock(MediaRoute2Info.class); + mInfoMediaManager.registerCallback(mCallback); + when(info.getId()).thenReturn(TEST_ID); when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); @@ -160,11 +167,14 @@ public class InfoMediaManagerTest { assertThat(infoDevice.getId()).isEqualTo(TEST_ID); assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice); assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); + verify(mCallback).onConnectedDeviceChanged(TEST_ID); } @Test public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() { final MediaRoute2Info info = mock(MediaRoute2Info.class); + mInfoMediaManager.registerCallback(mCallback); + when(info.getId()).thenReturn(TEST_ID); when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); when(info.isSystemRoute()).thenReturn(true); @@ -182,6 +192,7 @@ public class InfoMediaManagerTest { final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); assertThat(infoDevice.getId()).isEqualTo(TEST_ID); assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); + verify(mCallback).onConnectedDeviceChanged(null); } @Test @@ -493,4 +504,22 @@ public class InfoMediaManagerTest { assertThat(mInfoMediaManager.getSessionName()).isEqualTo(TEST_NAME); } + + @Test + public void onTransferFailed_shouldDispatchOnRequestFailed() { + mInfoMediaManager.registerCallback(mCallback); + + mInfoMediaManager.mMediaRouterCallback.onTransferFailed(null, null); + + verify(mCallback).onRequestFailed(REASON_UNKNOWN_ERROR); + } + + @Test + public void onRequestFailed_shouldDispatchOnRequestFailed() { + mInfoMediaManager.registerCallback(mCallback); + + mInfoMediaManager.mMediaRouterCallback.onRequestFailed(REASON_NETWORK_ERROR); + + verify(mCallback).onRequestFailed(REASON_NETWORK_ERROR); + } } 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 3611dfefbb7f..18d8f14eb88d 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 @@ -19,7 +19,6 @@ package com.android.settingslib.media; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -121,8 +120,6 @@ public class LocalMediaManagerTest { verify(currentDevice).disconnect(); verify(device).connect(); - verify(mCallback).onSelectedDeviceStateChanged(any(), - eq(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED)); } @Test @@ -368,7 +365,8 @@ public class LocalMediaManagerTest { mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2); assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2); - verify(mCallback).onDeviceAttributesChanged(); + verify(mCallback).onSelectedDeviceStateChanged(device2, + LocalMediaManager.MediaDeviceState.STATE_CONNECTED); } @Test @@ -477,4 +475,13 @@ public class LocalMediaManagerTest { assertThat(mLocalMediaManager.mMediaDevices).hasSize(3); verify(mCallback).onDeviceListUpdate(any()); } + + @Test + public void onRequestFailed_shouldDispatchOnRequestFailed() { + mLocalMediaManager.registerCallback(mCallback); + + mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(1); + + verify(mCallback).onRequestFailed(1); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java index ead2be4d7b26..a50965ab2619 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java @@ -136,4 +136,14 @@ public class MediaManagerTest { assertThat(device).isNull(); } + + @Test + public void dispatchOnRequestFailed_registerCallback_shouldDispatchCallback() { + mMediaManager.registerCallback(mCallback); + + mMediaManager.dispatchOnRequestFailed(1); + + verify(mCallback).onRequestFailed(1); + } + } diff --git a/packages/SettingsProvider/res/values-ta/strings.xml b/packages/SettingsProvider/res/values-ta/strings.xml index fa6b8cd9c456..54d2242dced9 100644 --- a/packages/SettingsProvider/res/values-ta/strings.xml +++ b/packages/SettingsProvider/res/values-ta/strings.xml @@ -20,8 +20,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4567566098528588863">"அமைப்புகளின் சேமிப்பிடம்"</string> - <!-- no translation found for wifi_softap_config_change (5688373762357941645) --> - <skip /> - <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) --> - <skip /> + <string name="wifi_softap_config_change" msgid="5688373762357941645">"ஹாட்ஸ்பாட் அமைப்புகள் மாற்றப்பட்டன"</string> + <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"விவரங்களைப் பார்க்க, தட்டவும்"</string> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 2dc6f393f7cc..5a9d7497b641 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2778,6 +2778,11 @@ public class SettingsProvider extends ContentProvider { public boolean insertSettingLocked(int type, int userId, String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName, boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) { + if (overrideableByRestore != Settings.DEFAULT_OVERRIDEABLE_BY_RESTORE) { + getContext().enforceCallingOrSelfPermission( + Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE, + "Caller is not allowed to modify settings overrideable by restore"); + } final int key = makeKey(type, userId); boolean success = false; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index cd62420f39ac..2d351c74bf54 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -1259,7 +1259,8 @@ final class SettingsState { public boolean reset() { // overrideableByRestore = true as resetting to default value isn't considered a // modification. - return update(this.defaultValue, false, packageName, null, true, true); + return update(this.defaultValue, false, packageName, null, true, true, + /* resetToDefault */ true); } public boolean isTransient() { @@ -1272,6 +1273,13 @@ final class SettingsState { public boolean update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage, boolean overrideableByRestore) { + return update(value, setDefault, packageName, tag, forceNonSystemPackage, + overrideableByRestore, /* resetToDefault */ false); + } + + private boolean update(String value, boolean setDefault, String packageName, String tag, + boolean forceNonSystemPackage, boolean overrideableByRestore, + boolean resetToDefault) { if (NULL_VALUE.equals(value)) { value = null; } @@ -1305,7 +1313,7 @@ final class SettingsState { } // isValuePreservedInRestore shouldn't change back to false if it has been set to true. - boolean isPreserved = this.isValuePreservedInRestore || !overrideableByRestore; + boolean isPreserved = shouldPreserveSetting(overrideableByRestore, resetToDefault); // Is something gonna change? if (Objects.equals(value, this.value) @@ -1329,6 +1337,17 @@ final class SettingsState { + " packageName=" + packageName + " tag=" + tag + " defaultFromSystem=" + defaultFromSystem + "}"; } + + private boolean shouldPreserveSetting(boolean overrideableByRestore, + boolean resetToDefault) { + if (resetToDefault) { + // By default settings are not marked as preserved. + return false; + } + + // isValuePreservedInRestore shouldn't change back to false if it has been set to true. + return this.isValuePreservedInRestore || !overrideableByRestore; + } } /** diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index b855d87fc214..6a3c6619c0ef 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -241,6 +241,18 @@ public class SettingsStateTest extends AndroidTestCase { assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); } + public void testResetSetting_preservedFlagIsReset() { + SettingsState settingsState = getSettingStateObject(); + // Initialize the setting. + settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE); + // Update the setting so that preserved flag is set. + settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE); + + settingsState.resetSettingLocked(SETTING_NAME); + assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); + + } + private SettingsState getSettingStateObject() { SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index e13e49f420ef..8f859b25faef 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -204,7 +204,6 @@ <!-- Permission needed to run network tests in CTS --> <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" /> - <uses-permission android:name="android.permission.NETWORK_STACK" /> <!-- Permission needed to test tcp keepalive offload. --> <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" /> @@ -276,6 +275,9 @@ <!-- Permission needed to test registering pull atom callbacks --> <uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM" /> + <!-- Permission needed to modify settings overrideable by restore in CTS tests --> + <uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index e0662309f571..0eadcc741747 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -46,6 +46,7 @@ android_library { "SystemUIPluginLib", "SystemUISharedLib", "SettingsLib", + "androidx.viewpager2_viewpager2", "androidx.legacy_legacy-support-v4", "androidx.recyclerview_recyclerview", "androidx.preference_preference", @@ -106,6 +107,7 @@ android_library { "SystemUIPluginLib", "SystemUISharedLib", "SettingsLib", + "androidx.viewpager2_viewpager2", "androidx.legacy_legacy-support-v4", "androidx.recyclerview_recyclerview", "androidx.preference_preference", diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 30b461d1fe45..da93db7b37f6 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -147,6 +147,7 @@ <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" /> <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER" /> <!-- Screen Capturing --> <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" /> diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml index 6533c18a41a9..34a966ceea75 100644 --- a/packages/SystemUI/res/layout/controls_management.xml +++ b/packages/SystemUI/res/layout/controls_management.xml @@ -25,13 +25,40 @@ android:paddingStart="@dimen/controls_management_side_padding" android:paddingEnd="@dimen/controls_management_side_padding" > - <TextView - android:id="@+id/title" + <LinearLayout + android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLarge" - android:textSize="@dimen/controls_title_size" - android:textAlignment="center" /> + android:gravity="center_vertical"> + + <FrameLayout + android:id="@+id/icon_frame" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="start|center_vertical" + android:minWidth="56dp" + android:visibility="gone" + android:paddingTop="@dimen/controls_app_icon_frame_top_padding" + android:paddingBottom="@dimen/controls_app_icon_frame_bottom_padding" + android:paddingEnd="@dimen/controls_app_icon_frame_side_padding" + android:paddingStart="@dimen/controls_app_icon_frame_side_padding" > + + <ImageView + android:id="@android:id/icon" + android:layout_width="@dimen/controls_app_icon_size" + android:layout_height="@dimen/controls_app_icon_size" /> + </FrameLayout> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="@dimen/controls_title_size" + android:textAlignment="center" /> + + </LinearLayout> + <TextView android:id="@+id/subtitle" @@ -41,19 +68,11 @@ android:textAppearance="?android:attr/textAppearanceSmall" android:textAlignment="center" /> - <androidx.core.widget.NestedScrollView + <ViewStub + android:id="@+id/stub" android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1" - android:orientation="vertical" - android:layout_marginTop="@dimen/controls_management_list_margin"> - - <ViewStub - android:id="@+id/stub" - android:layout_width="match_parent" - android:layout_height="match_parent"/> - - </androidx.core.widget.NestedScrollView> + android:layout_weight="1"/> <FrameLayout android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/controls_management_apps.xml b/packages/SystemUI/res/layout/controls_management_apps.xml index 2bab433d21f3..42d73f3cc9ce 100644 --- a/packages/SystemUI/res/layout/controls_management_apps.xml +++ b/packages/SystemUI/res/layout/controls_management_apps.xml @@ -14,12 +14,18 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - -<androidx.recyclerview.widget.RecyclerView +<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/list" android:layout_width="match_parent" - android:layout_height="match_parent" - > + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="vertical" + android:layout_marginTop="@dimen/controls_management_list_margin"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/list" + android:layout_width="match_parent" + android:layout_height="match_parent" + /> -</androidx.recyclerview.widget.RecyclerView>
\ No newline at end of file +</androidx.core.widget.NestedScrollView> diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml index aab32f45e77a..d2ccfcb11c5c 100644 --- a/packages/SystemUI/res/layout/controls_management_favorites.xml +++ b/packages/SystemUI/res/layout/controls_management_favorites.xml @@ -17,7 +17,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="0dp" android:orientation="vertical"> <TextView @@ -29,11 +29,17 @@ android:gravity="center_horizontal" /> - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/listAll" - android:layout_width="match_parent" + <com.android.systemui.controls.management.ManagementPageIndicator + android:id="@+id/structure_page_indicator" + android:layout_width="wrap_content" android:layout_height="match_parent" + android:layout_gravity="center" android:layout_marginTop="@dimen/controls_management_list_margin" - android:nestedScrollingEnabled="false"/> + android:visibility="gone" /> + + <androidx.viewpager2.widget.ViewPager2 + android:id="@+id/structure_pager" + android:layout_width="match_parent" + android:layout_height="match_parent"/> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_onboarding.xml b/packages/SystemUI/res/layout/controls_onboarding.xml new file mode 100644 index 000000000000..577a3b404472 --- /dev/null +++ b/packages/SystemUI/res/layout/controls_onboarding.xml @@ -0,0 +1,65 @@ +<?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:layout_height="wrap_content" + android:layout_width="wrap_content" + android:padding="4dp" + android:orientation="vertical"> + + <View + android:id="@+id/arrow" + android:elevation="2dp" + android:layout_width="10dp" + android:layout_height="8dp" + android:layout_marginBottom="-2dp" + android:layout_gravity="center_horizontal"/> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingStart="24dp" + android:paddingEnd="4dp" + android:background="@drawable/recents_onboarding_toast_rounded_background" + android:layout_gravity="center_horizontal" + android:elevation="2dp" + android:orientation="horizontal"> + + <TextView + android:id="@+id/onboarding_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_gravity="center_vertical" + android:textColor="?attr/wallpaperTextColor" + android:textSize="16sp"/> + <ImageView + android:id="@+id/dismiss" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="center_vertical" + android:padding="10dp" + android:layout_marginStart="2dp" + android:layout_marginEnd="2dp" + android:alpha="0.7" + android:src="@drawable/ic_close_white" + android:tint="?attr/wallpaperTextColor" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/accessibility_desc_close"/> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_structure_page.xml b/packages/SystemUI/res/layout/controls_structure_page.xml new file mode 100644 index 000000000000..047ab98eb191 --- /dev/null +++ b/packages/SystemUI/res/layout/controls_structure_page.xml @@ -0,0 +1,24 @@ +<?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. + --> + +<androidx.recyclerview.widget.RecyclerView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/listAll" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_marginTop="@dimen/controls_management_list_margin"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml index ff0c6a756ca2..92ae1b95264f 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml @@ -1,71 +1,75 @@ <?xml version="1.0" encoding="utf-8"?> -<ScrollView +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/global_actions_container" android:layout_width="match_parent" - android:layout_height="match_parent"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/global_actions_grid_root" + android:layout_height="match_parent" + android:orientation="vertical" +> + <com.android.systemui.globalactions.GlobalActionsFlatLayout + android:id="@id/global_actions_view" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:theme="@style/qs_theme" + android:gravity="top | center_horizontal" android:clipChildren="false" android:clipToPadding="false" - android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset" - android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset"> - - <com.android.systemui.globalactions.GlobalActionsFlatLayout - android:id="@id/global_actions_view" - android:layout_width="match_parent" + android:layout_marginTop="@dimen/global_actions_top_margin" + > + <LinearLayout + android:id="@android:id/list" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/global_actions_grid_side_margin" + android:layout_marginRight="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" android:orientation="horizontal" - android:theme="@style/qs_theme" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" - android:gravity="top | center_horizontal" - android:clipChildren="false" - android:clipToPadding="false" - android:layout_marginTop="@dimen/global_actions_top_margin"> - <LinearLayout - android:id="@android:id/list" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/global_actions_grid_side_margin" - android:layout_marginRight="@dimen/global_actions_grid_side_margin" - android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" - android:paddingRight="@dimen/global_actions_grid_horizontal_padding" - android:paddingTop="@dimen/global_actions_grid_vertical_padding" - android:paddingBottom="@dimen/global_actions_grid_vertical_padding" - android:orientation="horizontal" - android:gravity="left" - android:translationZ="@dimen/global_actions_translate" /> - </com.android.systemui.globalactions.GlobalActionsFlatLayout> + android:gravity="left" + android:translationZ="@dimen/global_actions_translate" + /> + </com.android.systemui.globalactions.GlobalActionsFlatLayout> + <com.android.systemui.globalactions.MinHeightScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset" + android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset" + android:orientation="vertical" + > <LinearLayout - android:id="@+id/global_actions_panel" + android:id="@+id/global_actions_grid_root" android:layout_width="match_parent" android:layout_height="wrap_content" + android:clipChildren="false" android:orientation="vertical" - app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" - app:layout_constraintTop_toBottomOf="@id/global_actions_view"> + android:clipToPadding="false" + > + <LinearLayout + android:id="@+id/global_actions_panel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + > + <FrameLayout + android:id="@+id/global_actions_panel_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + </LinearLayout> - <FrameLayout - android:id="@+id/global_actions_panel_container" + <LinearLayout + android:id="@+id/global_actions_controls" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginRight="@dimen/global_actions_grid_horizontal_padding" + android:layout_marginLeft="@dimen/global_actions_grid_horizontal_padding" + /> </LinearLayout> - - <LinearLayout - android:id="@+id/global_actions_controls" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" - app:layout_constraintTop_toBottomOf="@id/global_actions_panel" - app:layout_constraintBottom_toBottomOf="parent" /> - </androidx.constraintlayout.widget.ConstraintLayout> -</ScrollView> + </com.android.systemui.globalactions.MinHeightScrollView> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml deleted file mode 100644 index d4410702a7d1..000000000000 --- a/packages/SystemUI/res/layout/global_actions_wrapped.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<com.android.systemui.HardwareUiLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@id/global_actions_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="top|right" - android:layout_marginBottom="0dp" - android:orientation="vertical" - android:paddingTop="@dimen/global_actions_top_padding" - android:clipToPadding="false" - android:theme="@style/qs_theme" - android:clipChildren="false"> - - <!-- Global actions is right-aligned to be physically near power button --> - <LinearLayout - android:id="@android:id/list" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="top|right" - android:gravity="center" - android:orientation="vertical" - android:padding="@dimen/global_actions_padding" - android:translationZ="@dimen/global_actions_translate" /> - - <!-- For separated button--> - <FrameLayout - android:id="@+id/separated_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="top|right" - android:layout_marginTop="6dp" - android:gravity="center" - android:orientation="vertical" - android:padding="@dimen/global_actions_padding" - android:translationZ="@dimen/global_actions_translate" /> - -</com.android.systemui.HardwareUiLayout> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index d16082915207..20cafd046452 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -117,7 +117,7 @@ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> - wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,controls,screenrecord + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord </string> <!-- The tiles to display in QuickSettings --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9437485165a0..291db65da225 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1249,6 +1249,7 @@ <dimen name="controls_app_icon_size">32dp</dimen> <dimen name="controls_app_icon_frame_side_padding">8dp</dimen> <dimen name="controls_app_icon_frame_top_padding">4dp</dimen> + <dimen name="controls_app_icon_frame_bottom_padding">@dimen/controls_app_icon_frame_top_padding</dimen> <dimen name="controls_app_bottom_margin">8dp</dimen> <dimen name="controls_app_text_padding">8dp</dimen> <dimen name="controls_app_divider_height">2dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index caf22fe16beb..18fec29dd3cf 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2653,4 +2653,7 @@ <string name="controls_pin_verify">Verify device PIN</string> <!-- Controls PIN entry dialog, text hint [CHAR LIMIT=30] --> <string name="controls_pin_instructions">Enter PIN</string> + + <!-- Tooltip to show in management screen when there are multiple structures [CHAR_LIMIT=50] --> + <string name="controls_structure_tooltip">Swipe to see other structures</string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 125dd8f1d60c..47709108db1b 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -650,6 +650,7 @@ <!-- Controls styles --> <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar"> <item name="android:windowIsTranslucent">false</item> + <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item> </style> <style name="TextAppearance.Control"> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index ca88f13932ad..aed7c216433b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -44,7 +44,9 @@ public class RemoteAnimationTargetCompat { public final Rect clipRect; public final int prefixOrderIndex; public final Point position; + public final Rect localBounds; public final Rect sourceContainerBounds; + public final Rect screenSpaceBounds; public final boolean isNotInRecents; public final Rect contentInsets; @@ -57,7 +59,9 @@ public class RemoteAnimationTargetCompat { isTranslucent = app.isTranslucent; clipRect = app.clipRect; position = app.position; + localBounds = app.localBounds; sourceContainerBounds = app.sourceContainerBounds; + screenSpaceBounds = app.screenSpaceBounds; prefixOrderIndex = app.prefixOrderIndex; isNotInRecents = app.isNotInRecents; contentInsets = app.contentInsets; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java index 7cfa289595e3..d33c653f2cb7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.PixelFormat; import android.os.Bundle; import android.os.IBinder; +import android.util.Size; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.View; @@ -43,13 +44,27 @@ public class SurfaceViewRequestReceiver { mOpacity = opacity; } - /** Called whenever a surface view request is received. */ + /** See {@link #onReceive(Context, Bundle, View, Size)}. */ public void onReceive(Context context, Bundle bundle, View view) { + onReceive(context, bundle, view, null); + } + + /** + * Called whenever a surface view request is received. + * @param view the view rendering content, on the receiver end of the surface request. + * @param viewSize when {@param viewSize} is not specified, we will use the surface control size + * to attach the view to the window. + */ + public void onReceive(Context context, Bundle bundle, View view, Size viewSize) { if (mSurfaceControlViewHost != null) { mSurfaceControlViewHost.die(); } SurfaceControl surfaceControl = SurfaceViewRequestUtils.getSurfaceControl(bundle); if (surfaceControl != null) { + if (viewSize == null) { + viewSize = new Size(surfaceControl.getWidth(), surfaceControl.getHeight()); + } + IBinder hostToken = SurfaceViewRequestUtils.getHostToken(bundle); WindowlessWindowManager windowlessWindowManager = @@ -59,12 +74,22 @@ public class SurfaceViewRequestReceiver { context.getDisplayNoVerify(), windowlessWindowManager); WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( - surfaceControl.getWidth(), - surfaceControl.getHeight(), + viewSize.getWidth(), + viewSize.getHeight(), WindowManager.LayoutParams.TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, mOpacity); + // This aspect scales the view to fit in the surface and centers it + final float scale = Math.min(surfaceControl.getWidth() / (float) viewSize.getWidth(), + surfaceControl.getHeight() / (float) viewSize.getHeight()); + view.setScaleX(scale); + view.setScaleY(scale); + view.setPivotX(0); + view.setPivotY(0); + view.setTranslationX((surfaceControl.getWidth() - scale * viewSize.getWidth()) / 2); + view.setTranslationY((surfaceControl.getHeight() - scale * viewSize.getHeight()) / 2); + mSurfaceControlViewHost.addView(view, layoutParams); } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java index 0cbd541cbc8d..4409276f8c27 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java @@ -36,7 +36,7 @@ public class SurfaceViewRequestUtils { } /** - * Retrieves the SurfaceControl from an Intent created by + * Retrieves the SurfaceControl from a bundle created by * {@link #createSurfaceBundle(SurfaceView)}. **/ public static SurfaceControl getSurfaceControl(Bundle bundle) { @@ -44,7 +44,7 @@ public class SurfaceViewRequestUtils { } /** - * Retrieves the input token from an Intent created by + * Retrieves the input token from a bundle created by * {@link #createSurfaceBundle(SurfaceView)}. **/ public static @Nullable IBinder getHostToken(Bundle bundle) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 1c6223b847de..9e9b9dc4f3b0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -37,7 +37,8 @@ public abstract class TaskStackChangeListener { public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { } public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } public void onActivityUnpinned() { } - public void onPinnedActivityRestartAttempt(boolean clearedTask) { } + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) { } public void onActivityForcedResizable(String packageName, int taskId, int reason) { } public void onActivityDismissingDockedStack() { } public void onActivityLaunchOnSecondaryDisplayFailed() { } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index cbdd3f8191f4..ce9cbabaa5e9 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -30,6 +30,7 @@ import android.os.RemoteException; import android.os.Trace; import android.util.Log; +import com.android.internal.os.SomeArgs; import com.android.systemui.shared.recents.model.ThumbnailData; import java.util.ArrayList; @@ -120,11 +121,14 @@ public class TaskStackChangeListeners extends TaskStackListener { } @Override - public void onPinnedActivityRestartAttempt(boolean clearedTask) - throws RemoteException { - mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); - mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0) - .sendToTarget(); + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) throws RemoteException { + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = task; + args.argi1 = homeTaskVisible ? 1 : 0; + args.argi2 = clearedTask ? 1 : 0; + mHandler.removeMessages(H.ON_ACTIVITY_RESTART_ATTEMPT); + mHandler.obtainMessage(H.ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget(); } @Override @@ -236,7 +240,7 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_TASK_STACK_CHANGED = 1; private static final int ON_TASK_SNAPSHOT_CHANGED = 2; private static final int ON_ACTIVITY_PINNED = 3; - private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4; + private static final int ON_ACTIVITY_RESTART_ATTEMPT = 4; private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6; private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7; private static final int ON_TASK_PROFILE_LOCKED = 8; @@ -296,10 +300,14 @@ public class TaskStackChangeListeners extends TaskStackListener { } break; } - case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: { + case ON_ACTIVITY_RESTART_ATTEMPT: { + final SomeArgs args = (SomeArgs) msg.obj; + final RunningTaskInfo task = (RunningTaskInfo) args.arg1; + final boolean homeTaskVisible = args.argi1 != 0; + final boolean clearedTask = args.argi2 != 0; for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedActivityRestartAttempt( - msg.arg1 != 0); + mTaskStackListeners.get(i).onActivityRestartAttempt(task, + homeTaskVisible, clearedTask); } break; } @@ -419,6 +427,9 @@ public class TaskStackChangeListeners extends TaskStackListener { } } } + if (msg.obj instanceof SomeArgs) { + ((SomeArgs) msg.obj).recycle(); + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 4508fc74e884..e79005a9ffc2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -379,7 +379,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (DEBUG_SIM_STATES) { Log.v(TAG, "onSubscriptionInfoChanged()"); List<SubscriptionInfo> sil = mSubscriptionManager - .getActiveAndHiddenSubscriptionInfoList(); + .getCompleteActiveSubscriptionInfoList(); if (sil != null) { for (SubscriptionInfo subInfo : sil) { Log.v(TAG, "SubInfo:" + subInfo); @@ -433,10 +433,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) { List<SubscriptionInfo> sil = mSubscriptionInfo; if (sil == null || forceReload) { - sil = mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList(); + sil = mSubscriptionManager.getCompleteActiveSubscriptionInfoList(); } if (sil == null) { - // getActiveAndHiddenSubscriptionInfoList was null callers expect an empty list. + // getCompleteActiveSubscriptionInfoList was null callers expect an empty list. mSubscriptionInfo = new ArrayList<SubscriptionInfo>(); } else { mSubscriptionInfo = sil; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 03674648d1e4..2200b22b8b27 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -43,6 +43,7 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.InjectionInflationController; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -74,8 +75,8 @@ public final class ClockManager { private final ContentObserver mContentObserver = new ContentObserver(mMainHandler) { @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - super.onChange(selfChange, uri, userId); + public void onChange(boolean selfChange, Collection<Uri> uris, + int flags, int userId) { if (Objects.equals(userId, mCurrentUserObservable.getCurrentUser().getValue())) { reload(); diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index b6152dae33d6..a868cf58cf7c 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -71,11 +71,12 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment; import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; -import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; @@ -288,6 +289,7 @@ public class Dependency { @Inject Lazy<NotificationLogger> mNotificationLogger; @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager; @Inject Lazy<NotificationFilter> mNotificationFilter; + @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider; @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil; @Inject Lazy<SmartReplyController> mSmartReplyController; @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler; @@ -487,6 +489,8 @@ public class Dependency { mProviders.put(NotificationViewHierarchyManager.class, mNotificationViewHierarchyManager::get); mProviders.put(NotificationFilter.class, mNotificationFilter::get); + mProviders.put(NotificationInterruptionStateProvider.class, + mNotificationInterruptionStateProvider::get); mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get); mProviders.put(SmartReplyController.class, mSmartReplyController::get); mProviders.put(RemoteInputQuickSettingsDisabler.class, diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java deleted file mode 100644 index ad2e0024065f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.provider.Settings; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewOutlineProvider; -import android.view.ViewTreeObserver; -import android.widget.LinearLayout; - -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerService.Tunable; -import com.android.systemui.util.leak.RotationUtils; - -/** - * Layout for placing two containers at a specific physical position on the device, relative to the - * device's hardware, regardless of screen rotation. - */ -public class HardwareUiLayout extends MultiListLayout implements Tunable { - - private static final String EDGE_BLEED = "sysui_hwui_edge_bleed"; - private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider"; - private final int[] mTmp2 = new int[2]; - private ViewGroup mList; - private ViewGroup mSeparatedView; - private int mOldHeight; - private boolean mAnimating; - private AnimatorSet mAnimation; - private View mDivision; - private HardwareBgDrawable mListBackground; - private HardwareBgDrawable mSeparatedViewBackground; - private Animator mAnimator; - private boolean mCollapse; - private int mEndPoint; - private boolean mEdgeBleed; - private boolean mRoundedDivider; - private boolean mRotatedBackground; - private boolean mSwapOrientation = true; - - public HardwareUiLayout(Context context, AttributeSet attrs) { - super(context, attrs); - // Manually re-initialize mRotation to portrait-mode, since this view must always - // be constructed in portrait mode and rotated into the correct initial position. - mRotation = ROTATION_NONE; - updateSettings(); - } - - @Override - protected ViewGroup getSeparatedView() { - return findViewById(com.android.systemui.R.id.separated_button); - } - - @Override - protected ViewGroup getListView() { - return findViewById(android.R.id.list); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - updateSettings(); - Dependency.get(TunerService.class).addTunable(this, EDGE_BLEED, ROUNDED_DIVIDER); - getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener); - Dependency.get(TunerService.class).removeTunable(this); - } - - @Override - public void onTuningChanged(String key, String newValue) { - updateSettings(); - } - - private void updateSettings() { - mEdgeBleed = Settings.Secure.getInt(getContext().getContentResolver(), - EDGE_BLEED, 0) != 0; - mRoundedDivider = Settings.Secure.getInt(getContext().getContentResolver(), - ROUNDED_DIVIDER, 0) != 0; - updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); - mListBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, getContext()); - mSeparatedViewBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, - getContext()); - if (mList != null) { - mList.setBackground(mListBackground); - mSeparatedView.setBackground(mSeparatedViewBackground); - requestLayout(); - } - } - - private void updateEdgeMargin(int edge) { - if (mList != null) { - MarginLayoutParams params = (MarginLayoutParams) mList.getLayoutParams(); - if (mRotation == ROTATION_LANDSCAPE) { - params.topMargin = edge; - } else if (mRotation == ROTATION_SEASCAPE) { - params.bottomMargin = edge; - } else { - params.rightMargin = edge; - } - mList.setLayoutParams(params); - } - - if (mSeparatedView != null) { - MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams(); - if (mRotation == ROTATION_LANDSCAPE) { - params.topMargin = edge; - } else if (mRotation == ROTATION_SEASCAPE) { - params.bottomMargin = edge; - } else { - params.rightMargin = edge; - } - mSeparatedView.setLayoutParams(params); - } - } - - private int getEdgePadding() { - return getContext().getResources().getDimensionPixelSize(R.dimen.edge_margin); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (mList == null) { - if (getChildCount() != 0) { - mList = getListView(); - mList.setBackground(mListBackground); - mSeparatedView = getSeparatedView(); - mSeparatedView.setBackground(mSeparatedViewBackground); - updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); - mOldHeight = mList.getMeasuredHeight(); - - // Must be called to initialize view rotation correctly. - // Requires LayoutParams, hence why this isn't called during the constructor. - updateRotation(); - } else { - return; - } - } - int newHeight = mList.getMeasuredHeight(); - if (newHeight != mOldHeight) { - animateChild(mOldHeight, newHeight); - } - - post(() -> updatePaddingAndGravityIfTooTall()); - post(() -> updatePosition()); - } - - public void setSwapOrientation(boolean swapOrientation) { - mSwapOrientation = swapOrientation; - } - - private void updateRotation() { - int rotation = RotationUtils.getRotation(getContext()); - if (rotation != mRotation) { - rotate(mRotation, rotation); - mRotation = rotation; - } - } - - /** - * Requires LayoutParams to be set to work correctly, and therefore must be run after after - * the HardwareUILayout has been added to the view hierarchy. - */ - protected void rotate(int from, int to) { - super.rotate(from, to); - if (from != ROTATION_NONE && to != ROTATION_NONE) { - // Rather than handling this confusing case, just do 2 rotations. - rotate(from, ROTATION_NONE); - rotate(ROTATION_NONE, to); - return; - } - if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) { - rotateRight(); - } else { - rotateLeft(); - } - if (mAdapter.hasSeparatedItems()) { - if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) { - // Separated view has top margin, so seascape separated view need special rotation, - // not a full left or right rotation. - swapLeftAndTop(mSeparatedView); - } else if (from == ROTATION_LANDSCAPE) { - rotateRight(mSeparatedView); - } else { - rotateLeft(mSeparatedView); - } - } - if (to != ROTATION_NONE) { - if (mList instanceof LinearLayout) { - mRotatedBackground = true; - mListBackground.setRotatedBackground(true); - mSeparatedViewBackground.setRotatedBackground(true); - LinearLayout linearLayout = (LinearLayout) mList; - if (mSwapOrientation) { - linearLayout.setOrientation(LinearLayout.HORIZONTAL); - setOrientation(LinearLayout.HORIZONTAL); - } - swapDimens(mList); - swapDimens(mSeparatedView); - } - } else { - if (mList instanceof LinearLayout) { - mRotatedBackground = false; - mListBackground.setRotatedBackground(false); - mSeparatedViewBackground.setRotatedBackground(false); - LinearLayout linearLayout = (LinearLayout) mList; - if (mSwapOrientation) { - linearLayout.setOrientation(LinearLayout.VERTICAL); - setOrientation(LinearLayout.VERTICAL); - } - swapDimens(mList); - swapDimens(mSeparatedView); - } - } - } - - @Override - public void onUpdateList() { - super.onUpdateList(); - - for (int i = 0; i < mAdapter.getCount(); i++) { - ViewGroup parent; - boolean separated = mAdapter.shouldBeSeparated(i); - if (separated) { - parent = getSeparatedView(); - } else { - parent = getListView(); - } - View v = mAdapter.getView(i, null, parent); - parent.addView(v); - } - } - - private void rotateRight() { - rotateRight(this); - rotateRight(mList); - swapDimens(this); - - LayoutParams p = (LayoutParams) mList.getLayoutParams(); - p.gravity = rotateGravityRight(p.gravity); - mList.setLayoutParams(p); - - LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams(); - separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity); - mSeparatedView.setLayoutParams(separatedViewLayoutParams); - - setGravity(rotateGravityRight(getGravity())); - } - - private void swapDimens(View v) { - ViewGroup.LayoutParams params = v.getLayoutParams(); - int h = params.width; - params.width = params.height; - params.height = h; - v.setLayoutParams(params); - } - - private int rotateGravityRight(int gravity) { - int retGravity = 0; - int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; - - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - retGravity |= Gravity.CENTER_VERTICAL; - break; - case Gravity.RIGHT: - retGravity |= Gravity.BOTTOM; - break; - case Gravity.LEFT: - default: - retGravity |= Gravity.TOP; - break; - } - - switch (verticalGravity) { - case Gravity.CENTER_VERTICAL: - retGravity |= Gravity.CENTER_HORIZONTAL; - break; - case Gravity.BOTTOM: - retGravity |= Gravity.LEFT; - break; - case Gravity.TOP: - default: - retGravity |= Gravity.RIGHT; - break; - } - return retGravity; - } - - private void rotateLeft() { - rotateLeft(this); - rotateLeft(mList); - swapDimens(this); - - LayoutParams p = (LayoutParams) mList.getLayoutParams(); - p.gravity = rotateGravityLeft(p.gravity); - mList.setLayoutParams(p); - - LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams(); - separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity); - mSeparatedView.setLayoutParams(separatedViewLayoutParams); - - setGravity(rotateGravityLeft(getGravity())); - } - - private int rotateGravityLeft(int gravity) { - if (gravity == -1) { - gravity = Gravity.TOP | Gravity.START; - } - int retGravity = 0; - int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; - - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - retGravity |= Gravity.CENTER_VERTICAL; - break; - case Gravity.RIGHT: - retGravity |= Gravity.TOP; - break; - case Gravity.LEFT: - default: - retGravity |= Gravity.BOTTOM; - break; - } - - switch (verticalGravity) { - case Gravity.CENTER_VERTICAL: - retGravity |= Gravity.CENTER_HORIZONTAL; - break; - case Gravity.BOTTOM: - retGravity |= Gravity.RIGHT; - break; - case Gravity.TOP: - default: - retGravity |= Gravity.LEFT; - break; - } - return retGravity; - } - - private void rotateLeft(View v) { - v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(), - v.getPaddingLeft()); - MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams(); - params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin, - params.leftMargin); - v.setLayoutParams(params); - } - - private void rotateRight(View v) { - v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(), - v.getPaddingRight()); - MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams(); - params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin, - params.rightMargin); - v.setLayoutParams(params); - } - - private void swapLeftAndTop(View v) { - v.setPadding(v.getPaddingTop(), v.getPaddingLeft(), v.getPaddingBottom(), - v.getPaddingRight()); - MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams(); - params.setMargins(params.topMargin, params.leftMargin, params.bottomMargin, - params.rightMargin); - v.setLayoutParams(params); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - post(() -> updatePosition()); - - } - - private void animateChild(int oldHeight, int newHeight) { - if (true) return; - if (mAnimating) { - mAnimation.cancel(); - } - mAnimating = true; - mAnimation = new AnimatorSet(); - mAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAnimating = false; - } - }); - int fromTop = mList.getTop(); - int fromBottom = mList.getBottom(); - int toTop = fromTop - ((newHeight - oldHeight) / 2); - int toBottom = fromBottom + ((newHeight - oldHeight) / 2); - ObjectAnimator top = ObjectAnimator.ofInt(mList, "top", fromTop, toTop); - top.addUpdateListener(animation -> mListBackground.invalidateSelf()); - mAnimation.playTogether(top, - ObjectAnimator.ofInt(mList, "bottom", fromBottom, toBottom)); - } - - public void setDivisionView(View v) { - mDivision = v; - if (mDivision != null) { - mDivision.addOnLayoutChangeListener( - (v1, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> - updatePosition()); - } - updatePosition(); - } - - private void updatePosition() { - if (mList == null) return; - // If got separated button, setRotatedBackground to false, - // all items won't get white background. - boolean separated = mAdapter.hasSeparatedItems(); - mListBackground.setRotatedBackground(separated); - mSeparatedViewBackground.setRotatedBackground(separated); - if (mDivision != null && mDivision.getVisibility() == VISIBLE) { - int index = mRotatedBackground ? 0 : 1; - mDivision.getLocationOnScreen(mTmp2); - float trans = mRotatedBackground ? mDivision.getTranslationX() - : mDivision.getTranslationY(); - int viewTop = (int) (mTmp2[index] + trans); - mList.getLocationOnScreen(mTmp2); - viewTop -= mTmp2[index]; - setCutPoint(viewTop); - } else { - setCutPoint(mList.getMeasuredHeight()); - } - } - - private void setCutPoint(int point) { - int curPoint = mListBackground.getCutPoint(); - if (curPoint == point) return; - if (getAlpha() == 0 || curPoint == 0) { - mListBackground.setCutPoint(point); - return; - } - if (mAnimator != null) { - if (mEndPoint == point) { - return; - } - mAnimator.cancel(); - } - mEndPoint = point; - mAnimator = ObjectAnimator.ofInt(mListBackground, "cutPoint", curPoint, point); - if (mCollapse) { - mAnimator.setStartDelay(300); - mCollapse = false; - } - mAnimator.start(); - } - - // If current power menu height larger then screen height, remove padding to break power menu - // alignment and set menu center vertical within the screen. - private void updatePaddingAndGravityIfTooTall() { - int defaultTopPadding; - int viewsTotalHeight; - int separatedViewTopMargin; - int screenHeight; - int totalHeight; - int targetGravity; - boolean separated = mAdapter.hasSeparatedItems(); - MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams(); - switch (RotationUtils.getRotation(getContext())) { - case RotationUtils.ROTATION_LANDSCAPE: - defaultTopPadding = getPaddingLeft(); - viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); - separatedViewTopMargin = separated ? params.leftMargin : 0; - screenHeight = getMeasuredWidth(); - targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP; - break; - case RotationUtils.ROTATION_SEASCAPE: - defaultTopPadding = getPaddingRight(); - viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth(); - separatedViewTopMargin = separated ? params.leftMargin : 0; - screenHeight = getMeasuredWidth(); - targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM; - break; - default: // Portrait - defaultTopPadding = getPaddingTop(); - viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight(); - separatedViewTopMargin = separated ? params.topMargin : 0; - screenHeight = getMeasuredHeight(); - targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT; - break; - } - totalHeight = defaultTopPadding + viewsTotalHeight + separatedViewTopMargin; - if (totalHeight >= screenHeight) { - setPadding(0, 0, 0, 0); - setGravity(targetGravity); - } - } - - @Override - public ViewOutlineProvider getOutlineProvider() { - return super.getOutlineProvider(); - } - - public void setCollapse() { - mCollapse = true; - } - - private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> { - if (mHasOutsideTouch || (mList == null)) { - inoutInfo.setTouchableInsets( - ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); - return; - } - inoutInfo.setTouchableInsets( - ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT); - inoutInfo.contentInsets.set(mList.getLeft(), mList.getTop(), - 0, getBottom() - mList.getBottom()); - }; - - private float getAnimationDistance() { - return getContext().getResources().getDimension( - com.android.systemui.R.dimen.global_actions_panel_width) / 2; - } - - @Override - public float getAnimationOffsetX() { - if (RotationUtils.getRotation(mContext) == ROTATION_NONE) { - return getAnimationDistance(); - } - return 0; - } - - @Override - public float getAnimationOffsetY() { - switch (RotationUtils.getRotation(getContext())) { - case RotationUtils.ROTATION_LANDSCAPE: - return -getAnimationDistance(); - case RotationUtils.ROTATION_SEASCAPE: - return getAnimationDistance(); - default: // Portrait - return 0; - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 5e6589f76c13..6aa2326c388a 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -59,7 +59,8 @@ public final class Prefs { Key.TOUCHED_RINGER_TOGGLE, Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, Key.HAS_SEEN_BUBBLES_EDUCATION, - Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION + Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, + Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT }) public @interface Key { @Deprecated @@ -107,6 +108,7 @@ public final class Prefs { String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip"; String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding"; String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding"; + String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount"; } public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 7ca23085f6ee..4240209c4f1b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -279,7 +279,8 @@ class Bubble implements BubbleViewProvider { /** * @return the display id of the virtual display on which bubble contents is drawn. */ - int getDisplayId() { + @Override + public int getDisplayId() { return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index f873f42dd918..ae1438ee9bbd 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -83,11 +83,11 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoveInterceptor; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; @@ -169,7 +169,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // Callback that updates BubbleOverflowActivity on data change. @Nullable private Runnable mOverflowCallback = null; - private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private IStatusBarService mBarService; // Used for determining view rect for touch interaction @@ -279,7 +279,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi ShadeController shadeController, BubbleData data, ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, + NotificationInterruptionStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManager groupManager, @@ -304,7 +304,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, + NotificationInterruptionStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManager groupManager, @@ -316,7 +316,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi dumpManager.registerDumpable(TAG, this); mContext = context; mShadeController = shadeController; - mNotificationInterruptStateProvider = interruptionStateProvider; + mNotificationInterruptionStateProvider = interruptionStateProvider; mNotifUserManager = notifUserManager; mZenModeController = zenModeController; mFloatingContentCoordinator = floatingContentCoordinator; @@ -632,7 +632,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi for (NotificationEntry e : mNotificationEntryManager.getActiveNotificationsForCurrentUser()) { if (savedBubbleKeys.contains(e.getKey()) - && mNotificationInterruptStateProvider.shouldBubbleUp(e) + && mNotificationInterruptionStateProvider.shouldBubbleUp(e) && canLaunchInActivityView(mContext, e)) { updateBubble(e, /* suppressFlyout= */ true); } @@ -894,7 +894,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments( mContext, entry, previouslyUserCreated, userBlocked); - if (mNotificationInterruptStateProvider.shouldBubbleUp(entry) + if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry) && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) { if (wasAdjusted && !previouslyUserCreated) { // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated @@ -910,7 +910,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments( mContext, entry, previouslyUserCreated, userBlocked); - boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry) + boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry) && (canLaunchInActivityView(mContext, entry) || wasAdjusted); if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it @@ -1175,23 +1175,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * status bar, otherwise returns {@link Display#INVALID_DISPLAY}. */ public int getExpandedDisplayId(Context context) { - final Bubble bubble = getExpandedBubble(context); - return bubble != null ? bubble.getDisplayId() : INVALID_DISPLAY; - } - - @Nullable - private Bubble getExpandedBubble(Context context) { if (mStackView == null) { - return null; + return INVALID_DISPLAY; } final boolean defaultDisplay = context.getDisplay() != null && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY; - final Bubble expandedBubble = mStackView.getExpandedBubble(); - if (defaultDisplay && expandedBubble != null && isStackExpanded() + final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble(); + if (defaultDisplay && expandedViewProvider != null && isStackExpanded() && !mNotificationShadeWindowController.getPanelExpanded()) { - return expandedBubble; + return expandedViewProvider.getDisplayId(); } - return null; + return INVALID_DISPLAY; } @VisibleForTesting @@ -1233,6 +1227,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } @Override + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) { + for (Bubble b : mBubbleData.getBubbles()) { + if (b.getDisplayId() == task.displayId) { + expandStackAndSelectBubble(b.getKey()); + return; + } + } + } + + @Override public void onActivityLaunchOnSecondaryDisplayRerouted() { if (mStackView != null) { mBubbleData.setExpanded(false); @@ -1256,7 +1261,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Override public void onSingleTaskDisplayEmpty(int displayId) { - final Bubble expandedBubble = mStackView != null + final BubbleViewProvider expandedBubble = mStackView != null ? mStackView.getExpandedBubble() : null; int expandedId = expandedBubble != null ? expandedBubble.getDisplayId() : -1; @@ -1311,7 +1316,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - if (mStackView != null && mStackView.getBubbleCount() > 0) { + if (mStackView != null) { mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight)); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java index e800011981a9..19733a5f895c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java @@ -59,13 +59,15 @@ public class BubbleDebugConfig { return FORCE_SHOW_USER_EDUCATION || forceShow; } - static String formatBubblesString(List<Bubble> bubbles, Bubble selected) { + static String formatBubblesString(List<Bubble> bubbles, BubbleViewProvider selected) { StringBuilder sb = new StringBuilder(); for (Bubble bubble : bubbles) { if (bubble == null) { sb.append(" <null> !!!!!\n"); } else { - boolean isSelected = (selected != null && bubble == selected); + boolean isSelected = (selected != null + && selected.getKey() != BubbleOverflow.KEY + && bubble == selected); String arrow = isSelected ? "=>" : " "; sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n", arrow, diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java index 313bb4250570..4fb2d0881ede 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java @@ -16,6 +16,7 @@ package com.android.systemui.bubbles; +import static android.view.Display.INVALID_DISPLAY; import static android.view.View.GONE; import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE; @@ -45,7 +46,7 @@ public class BubbleOverflow implements BubbleViewProvider { public static final String KEY = "Overflow"; private BadgedImageView mOverflowBtn; - private BubbleExpandedView mOverflowExpandedView; + private BubbleExpandedView mExpandedView; private LayoutInflater mInflater; private Context mContext; private Bitmap mIcon; @@ -63,11 +64,11 @@ public class BubbleOverflow implements BubbleViewProvider { } void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) { - mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate( + mExpandedView = (BubbleExpandedView) mInflater.inflate( R.layout.bubble_expanded_view, parentViewGroup /* root */, false /* attachToRoot */); - mOverflowExpandedView.setOverflow(true); - mOverflowExpandedView.setStackView(stackView); + mExpandedView.setOverflow(true); + mExpandedView.setStackView(stackView); updateIcon(mContext, parentViewGroup); } @@ -124,7 +125,7 @@ public class BubbleOverflow implements BubbleViewProvider { @Override public BubbleExpandedView getExpandedView() { - return mOverflowExpandedView; + return mExpandedView; } @Override @@ -149,7 +150,7 @@ public class BubbleOverflow implements BubbleViewProvider { @Override public void setContentVisibility(boolean visible) { - mOverflowExpandedView.setContentVisibility(visible); + mExpandedView.setContentVisibility(visible); } @Override @@ -167,4 +168,9 @@ public class BubbleOverflow implements BubbleViewProvider { public String getKey() { return BubbleOverflow.KEY; } + + @Override + public int getDisplayId() { + return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY; + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 8cc10d9d148f..aedd2db738ee 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -126,6 +126,11 @@ public class BubbleStackView extends FrameLayout { @VisibleForTesting static final int FLYOUT_HIDE_AFTER = 5000; + private static final PhysicsAnimator.SpringConfig FLYOUT_IME_ANIMATION_SPRING_CONFIG = + new PhysicsAnimator.SpringConfig( + StackAnimationController.IME_ANIMATION_STIFFNESS, + StackAnimationController.DEFAULT_BOUNCINESS); + /** * Interface to synchronize {@link View} state and the screen. * @@ -903,14 +908,8 @@ public class BubbleStackView extends FrameLayout { * The {@link Bubble} that is expanded, null if one does not exist. */ @Nullable - Bubble getExpandedBubble() { - if (mExpandedBubble == null - || (BubbleExperimentConfig.allowBubbleOverflow(mContext) - && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn() - && BubbleOverflow.KEY.equals(mExpandedBubble.getKey()))) { - return null; - } - return (Bubble) mExpandedBubble; + BubbleViewProvider getExpandedBubble() { + return mExpandedBubble; } // via BubbleData.Listener @@ -1240,7 +1239,8 @@ public class BubbleStackView extends FrameLayout { } /** - * @deprecated use {@link #setExpanded(boolean)} and {@link #setSelectedBubble(Bubble)} + * @deprecated use {@link #setExpanded(boolean)} and + * {@link BubbleData#setSelectedBubble(Bubble)} */ @Deprecated @MainThread @@ -1282,7 +1282,7 @@ public class BubbleStackView extends FrameLayout { if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "animateCollapse"); Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(), - getExpandedBubble())); + mExpandedBubble)); } updateOverflowBtnVisibility(/* apply */ false); mBubbleContainer.cancelAllAnimations(); @@ -1354,8 +1354,23 @@ public class BubbleStackView extends FrameLayout { public void onImeVisibilityChanged(boolean visible, int height) { mStackAnimationController.setImeHeight(visible ? height + mImeOffset : 0); - if (!mIsExpanded) { - mStackAnimationController.animateForImeVisibility(visible); + if (!mIsExpanded && getBubbleCount() > 0) { + final float stackDestinationY = + mStackAnimationController.animateForImeVisibility(visible); + + // How far the stack is animating due to IME, we'll just animate the flyout by that + // much too. + final float stackDy = + stackDestinationY - mStackAnimationController.getStackPosition().y; + + // If the flyout is visible, translate it along with the bubble stack. + if (mFlyout.getVisibility() == VISIBLE) { + PhysicsAnimator.getInstance(mFlyout) + .spring(DynamicAnimation.TRANSLATION_Y, + mFlyout.getTranslationY() + stackDy, + FLYOUT_IME_ANIMATION_SPRING_CONFIG) + .start(); + } } } @@ -1371,13 +1386,14 @@ public class BubbleStackView extends FrameLayout { if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "onBubbleDragStart: bubble=" + bubble); } - maybeShowManageEducation(false); mExpandedAnimationController.prepareForBubbleDrag(bubble, mMagneticTarget); // We're dragging an individual bubble, so set the magnetized object to the magnetized // bubble. mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut(); mMagnetizedObject.setMagnetListener(mIndividualBubbleMagnetListener); + + maybeShowManageEducation(false); } /** Called with the coordinates to which an individual bubble has been dragged. */ @@ -1841,17 +1857,16 @@ public class BubbleStackView extends FrameLayout { } private void updatePointerPosition() { - Bubble expandedBubble = getExpandedBubble(); - if (expandedBubble == null) { + if (mExpandedBubble == null) { return; } - int index = getBubbleIndex(expandedBubble); + int index = getBubbleIndex(mExpandedBubble); float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index); float halfBubble = mBubbleSize / 2f; float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble; // Padding might be adjusted for insets, so get it directly from the view bubbleCenter -= mExpandedViewContainer.getPaddingLeft(); - expandedBubble.getExpandedView().setPointerPosition(bubbleCenter); + mExpandedBubble.getExpandedView().setPointerPosition(bubbleCenter); } /** @@ -1873,11 +1888,10 @@ public class BubbleStackView extends FrameLayout { * is between 0 and the bubble count minus 1. */ int getBubbleIndex(@Nullable BubbleViewProvider provider) { - if (provider == null || provider.getKey() == BubbleOverflow.KEY) { + if (provider == null) { return 0; } - Bubble b = (Bubble) provider; - return mBubbleContainer.indexOfChild(b.getIconView()); + return mBubbleContainer.indexOfChild(provider.getIconView()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java index f04933abdcc2..ef84c73b3145 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java @@ -41,4 +41,6 @@ interface BubbleViewProvider { Path getDotPath(); boolean showDot(); + + int getDisplayId(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index b81665cd186a..86387f1cc546 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -68,9 +68,10 @@ public class StackAnimationController extends /** * Values to use for the default {@link SpringForce} provided to the physics animation layout. */ - private static final int DEFAULT_STIFFNESS = 12000; + public static final int DEFAULT_STIFFNESS = 12000; + public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW; private static final int FLING_FOLLOW_STIFFNESS = 20000; - private static final float DEFAULT_BOUNCINESS = 0.9f; + public static final float DEFAULT_BOUNCINESS = 0.9f; /** * Friction applied to fling animations. Since the stack must land on one of the sides of the @@ -118,8 +119,11 @@ public class StackAnimationController extends /** Whether or not the stack's start position has been set. */ private boolean mStackMovedToStartPosition = false; - /** The most recent position in which the stack was resting on the edge of the screen. */ - @Nullable private PointF mRestingStackPosition; + /** + * The stack's most recent position along the edge of the screen. This is saved when the last + * bubble is removed, so that the stack can be restored in its previous position. + */ + private PointF mRestingStackPosition; /** The height of the most recently visible IME. */ private float mImeHeight = 0f; @@ -465,7 +469,6 @@ public class StackAnimationController extends .addEndListener((animation, canceled, endValue, endVelocity) -> { if (!canceled) { - mRestingStackPosition = new PointF(); mRestingStackPosition.set(mStackPosition); springFirstBubbleWithStackFollowing(property, spring, endVelocity, @@ -501,8 +504,10 @@ public class StackAnimationController extends /** * Animates the stack either away from the newly visible IME, or back to its original position * due to the IME going away. + * + * @return The destination Y value of the stack due to the IME movement. */ - public void animateForImeVisibility(boolean imeVisible) { + public float animateForImeVisibility(boolean imeVisible) { final float maxBubbleY = getAllowableStackPositionRegion().bottom; float destinationY = Float.MIN_VALUE; @@ -523,12 +528,14 @@ public class StackAnimationController extends springFirstBubbleWithStackFollowing( DynamicAnimation.TRANSLATION_Y, getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null) - .setStiffness(SpringForce.STIFFNESS_LOW), + .setStiffness(IME_ANIMATION_STIFFNESS), /* startVel */ 0f, destinationY); notifyFloatingCoordinatorStackAnimatingTo(mStackPosition.x, destinationY); } + + return destinationY; } /** @@ -583,7 +590,7 @@ public class StackAnimationController extends - mBubblePaddingTop - (mImeHeight > Float.MIN_VALUE ? mImeHeight + mBubblePaddingTop : 0f) - Math.max( - insets.getSystemWindowInsetBottom(), + insets.getStableInsetBottom(), insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetBottom() : 0); @@ -853,7 +860,12 @@ public class StackAnimationController extends public void setStackPosition(PointF pos) { Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y)); mStackPosition.set(pos.x, pos.y); - mRestingStackPosition = mStackPosition; + + if (mRestingStackPosition == null) { + mRestingStackPosition = new PointF(); + } + + mRestingStackPosition.set(mStackPosition); // If we're not the active controller, we don't want to physically move the bubble views. if (isActiveController()) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java index 27c9e9895324..ac97d8aab326 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java @@ -25,8 +25,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; @@ -54,7 +54,7 @@ public interface BubbleModule { ShadeController shadeController, BubbleData data, ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, + NotificationInterruptionStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManager groupManager, diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt index 49a16d892ef4..dec60073a55e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt @@ -16,10 +16,12 @@ package com.android.systemui.controls +import android.content.ComponentName import android.service.controls.Control data class ControlStatus( val control: Control, + val component: ComponentName, var favorite: Boolean, val removed: Boolean = false -)
\ No newline at end of file +) diff --git a/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt b/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt new file mode 100644 index 000000000000..6e17bc94b16b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt @@ -0,0 +1,160 @@ +/* + * 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.controls + +import android.annotation.StringRes +import android.content.Context +import android.graphics.CornerPathEffect +import android.graphics.drawable.ShapeDrawable +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import android.widget.TextView +import com.android.systemui.Prefs +import com.android.systemui.R +import com.android.systemui.recents.TriangleShape + +/** + * Manager for showing an onboarding tooltip on screen. + * + * The tooltip can be made to appear below or above a point. The number of times it will appear + * is determined by an shared preference (defined in [Prefs]). + * + * @property context A context to use to inflate the views and retrieve shared preferences from + * @property preferenceName name of the preference to use to track the number of times the tooltip + * has been shown. + * @property maxTimesShown the maximum number of times to show the tooltip + * @property below whether the tooltip should appear below (with up pointing arrow) or above (down + * pointing arrow) the specified point. + * @see [TooltipManager.show] + */ +class TooltipManager( + context: Context, + private val preferenceName: String, + private val maxTimesShown: Int = 2, + private val below: Boolean = true +) { + + companion object { + private const val SHOW_DELAY_MS: Long = 500 + private const val SHOW_DURATION_MS: Long = 300 + private const val HIDE_DURATION_MS: Long = 100 + } + + private var shown = Prefs.getInt(context, preferenceName, 0) + + val layout: ViewGroup = + LayoutInflater.from(context).inflate(R.layout.controls_onboarding, null) as ViewGroup + val preferenceStorer = { num: Int -> + Prefs.putInt(context, preferenceName, num) + } + + init { + layout.alpha = 0f + } + + private val textView = layout.requireViewById<TextView>(R.id.onboarding_text) + private val dismissView = layout.requireViewById<View>(R.id.dismiss).apply { + setOnClickListener { + hide(true) + } + } + + private val arrowView = layout.requireViewById<View>(R.id.arrow).apply { + val typedValue = TypedValue() + context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true) + val toastColor = context.resources.getColor(typedValue.resourceId, context.theme) + val arrowRadius = context.resources.getDimensionPixelSize( + R.dimen.recents_onboarding_toast_arrow_corner_radius) + val arrowLp = layoutParams + val arrowDrawable = ShapeDrawable(TriangleShape.create( + arrowLp.width.toFloat(), arrowLp.height.toFloat(), below)) + val arrowPaint = arrowDrawable.paint + arrowPaint.color = toastColor + // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable. + arrowPaint.pathEffect = CornerPathEffect(arrowRadius.toFloat()) + setBackground(arrowDrawable) + } + + init { + if (!below) { + layout.removeView(arrowView) + layout.addView(arrowView) + (arrowView.layoutParams as ViewGroup.MarginLayoutParams).apply { + bottomMargin = topMargin + topMargin = 0 + } + } + } + + /** + * Show the tooltip + * + * @param stringRes the id of the string to show in the tooltip + * @param x horizontal position (w.r.t. screen) for the arrow point + * @param y vertical position (w.r.t. screen) for the arrow point + */ + fun show(@StringRes stringRes: Int, x: Int, y: Int) { + if (!shouldShow()) return + textView.setText(stringRes) + shown++ + preferenceStorer(shown) + layout.post { + val p = IntArray(2) + layout.getLocationOnScreen(p) + layout.translationX = (x - p[0] - layout.width / 2).toFloat() + layout.translationY = (y - p[1]).toFloat() - if (!below) layout.height else 0 + if (layout.alpha == 0f) { + layout.animate() + .alpha(1f) + .withLayer() + .setStartDelay(SHOW_DELAY_MS) + .setDuration(SHOW_DURATION_MS) + .setInterpolator(DecelerateInterpolator()) + .start() + } + } + } + + /** + * Hide the tooltip + * + * @param animate whether to animate the fade out + */ + fun hide(animate: Boolean = false) { + if (layout.alpha == 0f) return + layout.post { + if (animate) { + layout.animate() + .alpha(0f) + .withLayer() + .setStartDelay(0) + .setDuration(HIDE_DURATION_MS) + .setInterpolator(AccelerateInterpolator()) + .start() + } else { + layout.animate().cancel() + layout.alpha = 0f + } + } + } + + private fun shouldShow() = shown < maxTimesShown +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt index fd6e2566b1b6..c5af436e7e92 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt @@ -38,8 +38,9 @@ interface ControlsBindingController : UserAwareController { * * @param component The [ComponentName] of the service to bind * @param callback a callback to return the loaded controls to (or an error). + * @return a runnable to cancel the load */ - fun bindAndLoad(component: ComponentName, callback: LoadCallback) + fun bindAndLoad(component: ComponentName, callback: LoadCallback): Runnable /** * Request to bind to the given service. diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt index 8f02c252beb1..f3bd0b57a0cb 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -116,8 +116,10 @@ open class ControlsBindingControllerImpl @Inject constructor( override fun bindAndLoad( component: ComponentName, callback: ControlsBindingController.LoadCallback - ) { - retrieveLifecycleManager(component)?.maybeBindAndLoad(LoadSubscriber(callback)) + ): Runnable { + val subscriber = LoadSubscriber(callback) + retrieveLifecycleManager(component)?.maybeBindAndLoad(subscriber) + return subscriber.loadCancel() } override fun subscribe(structureInfo: StructureInfo) { @@ -208,7 +210,6 @@ open class ControlsBindingControllerImpl @Inject constructor( ) : CallbackRunnable(token) { override fun doRun() { callback.accept(list) - provider?.unbindService() } } @@ -292,8 +293,14 @@ open class ControlsBindingControllerImpl @Inject constructor( ) : IControlsSubscriber.Stub() { val loadedControls = ArrayList<Control>() var hasError = false + private var _loadCancelInternal: (() -> Unit)? = null + fun loadCancel() = Runnable { + Log.d(TAG, "Cancel load requested") + _loadCancelInternal?.invoke() + } override fun onSubscribe(token: IBinder, subs: IControlsSubscription) { + _loadCancelInternal = subs::cancel backgroundExecutor.execute(OnSubscribeRunnable(token, subs)) } @@ -302,10 +309,12 @@ open class ControlsBindingControllerImpl @Inject constructor( } override fun onError(token: IBinder, s: String) { hasError = true + _loadCancelInternal = {} backgroundExecutor.execute(OnLoadErrorRunnable(token, s, callback)) } override fun onComplete(token: IBinder) { + _loadCancelInternal = {} if (!hasError) { backgroundExecutor.execute(OnLoadRunnable(token, loadedControls, callback)) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt index 7eafe2e65cca..9e0d26c3935f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt @@ -59,6 +59,11 @@ interface ControlsController : UserAwareController { ) /** + * Cancels a pending load call + */ + fun cancelLoad() + + /** * Request to subscribe for favorited controls per structure * * @param structureInfo structure to limit the subscription to 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 50bd1ade1e22..9cb902f51f22 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -72,6 +72,8 @@ class ControlsControllerImpl @Inject constructor ( private var userChanging: Boolean = true + private var loadCanceller: Runnable? = null + private var currentUser = UserHandle.of(ActivityManager.getCurrentUser()) override val currentUserId get() = currentUser.identifier @@ -127,7 +129,7 @@ class ControlsControllerImpl @Inject constructor ( internal val settingObserver = object : ContentObserver(null) { override fun onChange( selfChange: Boolean, - uris: MutableIterable<Uri>, + uris: Collection<Uri>, flags: Int, userId: Int ) { @@ -213,8 +215,9 @@ class ControlsControllerImpl @Inject constructor ( if (!confirmAvailability()) { if (userChanging) { // Try again later, userChanging should not last forever. If so, we have bigger - // problems - executor.executeDelayed( + // problems. This will return a runnable that allows to cancel the delayed version, + // it will not be able to cancel the load if + loadCanceller = executor.executeDelayed( { loadForComponent(componentName, dataCallback) }, USER_CHANGE_RETRY_DELAY, TimeUnit.MILLISECONDS @@ -224,10 +227,11 @@ class ControlsControllerImpl @Inject constructor ( } return } - bindingController.bindAndLoad( + loadCanceller = bindingController.bindAndLoad( componentName, object : ControlsBindingController.LoadCallback { override fun accept(controls: List<Control>) { + loadCanceller = null executor.execute { val favoritesForComponentKeys = Favorites .getControlsForComponent(componentName).map { it.controlId } @@ -238,7 +242,11 @@ class ControlsControllerImpl @Inject constructor ( } val removed = findRemoved(favoritesForComponentKeys.toSet(), controls) val controlsWithFavorite = controls.map { - ControlStatus(it, it.controlId in favoritesForComponentKeys) + ControlStatus( + it, + componentName, + it.controlId in favoritesForComponentKeys + ) } val loadData = createLoadDataObject( Favorites.getControlsForComponent(componentName) @@ -247,28 +255,37 @@ class ControlsControllerImpl @Inject constructor ( controlsWithFavorite, favoritesForComponentKeys ) - dataCallback.accept(loadData) } } override fun error(message: String) { - val loadData = Favorites.getControlsForComponent(componentName).let { - controls -> + 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 + controls.map { + createRemovedStatus(componentName, it, false) + }, + keys, + true ) + } + dataCallback.accept(loadData) } - - dataCallback.accept(loadData) } } ) } + override fun cancelLoad() { + loadCanceller?.let { + executor.execute(it) + } + } + private fun createRemovedStatus( componentName: ComponentName, controlInfo: ControlInfo, @@ -286,7 +303,7 @@ class ControlsControllerImpl @Inject constructor ( .setTitle(controlInfo.controlTitle) .setDeviceType(controlInfo.deviceType) .build() - return ControlStatus(control, true, setRemoved) + return ControlStatus(control, componentName, true, setRemoved) } private fun findRemoved(favoriteKeys: Set<String>, list: List<Control>): Set<String> { @@ -485,10 +502,12 @@ private object Favorites { updatedStructure } else { s } - structures.add(newStructure) + if (!newStructure.controls.isEmpty()) { + structures.add(newStructure) + } } - if (!replaced) { + if (!replaced && !updatedStructure.controls.isEmpty()) { structures.add(updatedStructure) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt index c05351795aed..01f906958fc1 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt @@ -22,14 +22,20 @@ import com.android.systemui.controls.ControlStatus import com.android.systemui.controls.controller.ControlInfo /** - * This model is used to show all controls separated by zones. + * This model is used to show controls separated by zones. * * The model will sort the controls and zones in the following manner: * * The zones will be sorted in a first seen basis * * The controls in each zone will be sorted in a first seen basis. * - * @property controls List of all controls as returned by loading - * @property initialFavoriteIds sorted ids of favorite controls + * The controls passed should belong to the same structure, as an instance of this model will be + * created for each structure. + * + * The list of favorite ids can contain ids for controls not passed to this model. Those will be + * filtered out. + * + * @property controls List of controls as returned by loading + * @property initialFavoriteIds sorted ids of favorite controls. * @property noZoneString text to use as header for all controls that have blank or `null` zone. */ class AllModel( @@ -50,7 +56,10 @@ class AllModel( } } - private val favoriteIds = initialFavoriteIds.toMutableList() + private val favoriteIds = run { + val ids = controls.mapTo(HashSet()) { it.control.controlId } + initialFavoriteIds.filter { it in ids }.toMutableList() + } override val elements: List<ElementWrapper> = createWrappers(controls) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index c21f7241180b..563c2f677801 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -16,8 +16,8 @@ package com.android.systemui.controls.management +import android.content.ComponentName import android.graphics.Rect -import android.graphics.drawable.Icon import android.service.controls.DeviceTypes import android.view.LayoutInflater import android.view.View @@ -42,7 +42,6 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit * @param onlyFavorites set to true to only display favorites instead of all controls */ class ControlAdapter( - private val layoutInflater: LayoutInflater, private val elevation: Float ) : RecyclerView.Adapter<Holder>() { @@ -60,6 +59,7 @@ class ControlAdapter( private var model: ControlsModel? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val layoutInflater = LayoutInflater.from(parent.context) return when (viewType) { TYPE_CONTROL -> { ControlHolder( @@ -147,7 +147,7 @@ private class ControlHolder(view: View, val favoriteCallback: ModelFavoriteChang override fun bindData(wrapper: ElementWrapper) { wrapper as ControlWrapper val data = wrapper.controlStatus - val renderInfo = getRenderInfo(data.control.deviceType) + val renderInfo = getRenderInfo(data.component, data.control.deviceType) title.text = data.control.title subtitle.text = data.control.subtitle favorite.isChecked = data.favorite @@ -160,16 +160,17 @@ private class ControlHolder(view: View, val favoriteCallback: ModelFavoriteChang } private fun getRenderInfo( + component: ComponentName, @DeviceTypes.DeviceType deviceType: Int ): RenderInfo { - return RenderInfo.lookup(deviceType, true) + return RenderInfo.lookup(itemView.context, component, deviceType, true) } private fun applyRenderInfo(ri: RenderInfo) { val context = itemView.context val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) - icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId)) + icon.setImageDrawable(ri.icon) icon.setImageTintList(fg) } } @@ -191,4 +192,4 @@ class MarginItemDecorator( right = sideMargins } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index 471f9d30b08b..f2303e622f8d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -19,20 +19,29 @@ package com.android.systemui.controls.management import android.app.Activity import android.content.ComponentName import android.content.Intent +import android.content.res.Configuration +import android.graphics.drawable.Drawable import android.os.Bundle -import android.view.LayoutInflater +import android.text.TextUtils +import android.view.Gravity import android.view.View +import android.view.ViewGroup import android.view.ViewStub import android.widget.Button +import android.widget.FrameLayout +import android.widget.ImageView import android.widget.TextView -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.android.systemui.Prefs import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.controls.controller.StructureInfo +import com.android.systemui.controls.ControlsServiceInfo +import com.android.systemui.controls.TooltipManager import com.android.systemui.controls.controller.ControlsControllerImpl +import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.settings.CurrentUserTracker +import java.text.Collator import java.util.concurrent.Executor import java.util.function.Consumer import javax.inject.Inject @@ -40,20 +49,31 @@ import javax.inject.Inject class ControlsFavoritingActivity @Inject constructor( @Main private val executor: Executor, private val controller: ControlsControllerImpl, + private val listingController: ControlsListingController, broadcastDispatcher: BroadcastDispatcher ) : Activity() { companion object { private const val TAG = "ControlsFavoritingActivity" const val EXTRA_APP = "extra_app_label" + private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT + private const val TOOLTIP_MAX_SHOWN = 2 } - private lateinit var recyclerViewAll: RecyclerView - private lateinit var adapterAll: ControlAdapter - private lateinit var statusText: TextView - private var model: ControlsModel? = null private var component: ComponentName? = null - private var structureName: CharSequence = "" + private var appName: CharSequence? = null + + private lateinit var structurePager: ViewPager2 + private lateinit var statusText: TextView + private lateinit var titleView: TextView + private lateinit var iconView: ImageView + private lateinit var iconFrame: View + private lateinit var pageIndicator: ManagementPageIndicator + private var mTooltipManager: TooltipManager? = null + private lateinit var doneButton: View + private var listOfStructures = emptyList<StructureContainer>() + + private lateinit var comparator: Comparator<StructureContainer> private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val startingUser = controller.currentUserId @@ -66,96 +86,206 @@ class ControlsFavoritingActivity @Inject constructor( } } + private val listingCallback = object : ControlsListingController.ControlsListingCallback { + private var icon: Drawable? = null + + override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) { + val newIcon = serviceInfos.firstOrNull { it.componentName == component }?.loadIcon() + if (icon == newIcon) return + icon = newIcon + executor.execute { + if (icon != null) { + iconView.setImageDrawable(icon) + } + iconFrame.visibility = if (icon != null) View.VISIBLE else View.GONE + } + } + } + override fun onBackPressed() { finish() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.controls_management) - requireViewById<ViewStub>(R.id.stub).apply { - layoutResource = R.layout.controls_management_favorites - inflate() - } - - val app = intent.getCharSequenceExtra(EXTRA_APP) + val collator = Collator.getInstance(resources.configuration.locales[0]) + comparator = compareBy(collator) { it.structureName } + appName = intent.getCharSequenceExtra(EXTRA_APP) component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME) - statusText = requireViewById(R.id.status_message) - setUpRecyclerView() + bindViews() - requireViewById<TextView>(R.id.title).text = app?.let { it } - ?: resources.getText(R.string.controls_favorite_default_title) - requireViewById<TextView>(R.id.subtitle).text = - resources.getText(R.string.controls_favorite_subtitle) + setUpPager() - requireViewById<Button>(R.id.other_apps).apply { - visibility = View.VISIBLE - setOnClickListener { - this@ControlsFavoritingActivity.onBackPressed() - } - } + loadControls() - requireViewById<Button>(R.id.done).setOnClickListener { - if (component == null) return@setOnClickListener - val favoritesForStorage = model?.favorites?.map { - it.build() - } - if (favoritesForStorage != null) { - controller.replaceFavoritesForStructure(StructureInfo(component!!, structureName, - favoritesForStorage)) - finishAffinity() - } - } + listingController.addCallback(listingCallback) + currentUserTracker.startTracking() + } + + private fun loadControls() { component?.let { statusText.text = resources.getText(com.android.internal.R.string.loading) + val emptyZoneString = resources.getText( + R.string.controls_favorite_other_zone_header) controller.loadForComponent(it, Consumer { data -> val allControls = data.allControls val favoriteKeys = data.favoritesIds val error = data.errorOnLoad - val structures = allControls.fold(hashSetOf<CharSequence>()) { - s, c -> - s.add(c.control.structure ?: "") - s - } - // TODO add multi structure switching support + val controlsByStructure = allControls.groupBy { it.control.structure ?: "" } + listOfStructures = controlsByStructure.map { + StructureContainer(it.key, AllModel(it.value, favoriteKeys, emptyZoneString)) + }.sortedWith(comparator) executor.execute { - val emptyZoneString = resources.getText( - R.string.controls_favorite_other_zone_header) - val model = AllModel(allControls, favoriteKeys, emptyZoneString) - adapterAll.changeModel(model) - this.model = model + doneButton.isEnabled = true + structurePager.adapter = StructureAdapter(listOfStructures) if (error) { statusText.text = resources.getText(R.string.controls_favorite_load_error) } else { statusText.visibility = View.GONE } + pageIndicator.setNumPages(listOfStructures.size) + pageIndicator.setLocation(0f) + pageIndicator.visibility = + if (listOfStructures.size > 1) View.VISIBLE else View.GONE } }) } + } - currentUserTracker.startTracking() + private fun setUpPager() { + structurePager.apply { + adapter = StructureAdapter(emptyList()) + registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + val name = listOfStructures[position].structureName + titleView.text = if (!TextUtils.isEmpty(name)) name else appName + } + + override fun onPageScrolled( + position: Int, + positionOffset: Float, + positionOffsetPixels: Int + ) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels) + pageIndicator.setLocation(position + positionOffset) + } + }) + } + } + + private fun bindViews() { + setContentView(R.layout.controls_management) + requireViewById<ViewStub>(R.id.stub).apply { + layoutResource = R.layout.controls_management_favorites + inflate() + } + + statusText = requireViewById(R.id.status_message) + if (shouldShowTooltip()) { + mTooltipManager = TooltipManager(statusText.context, + TOOLTIP_PREFS_KEY, TOOLTIP_MAX_SHOWN) + addContentView( + mTooltipManager?.layout, + FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.TOP or Gravity.LEFT + ) + ) + } + pageIndicator = requireViewById<ManagementPageIndicator>( + R.id.structure_page_indicator).apply { + addOnLayoutChangeListener(object : View.OnLayoutChangeListener { + override fun onLayoutChange( + v: View, + left: Int, + top: Int, + right: Int, + bottom: Int, + oldLeft: Int, + oldTop: Int, + oldRight: Int, + oldBottom: Int + ) { + if (v.visibility == View.VISIBLE && mTooltipManager != null) { + val p = IntArray(2) + v.getLocationOnScreen(p) + val x = p[0] + (right - left) / 2 + val y = p[1] + bottom - top + mTooltipManager?.show(R.string.controls_structure_tooltip, x, y) + } + } + }) + visibilityListener = { + if (it != View.VISIBLE) { + mTooltipManager?.hide(true) + } + } + } + + titleView = requireViewById<TextView>(R.id.title).apply { + text = appName ?: resources.getText(R.string.controls_favorite_default_title) + } + requireViewById<TextView>(R.id.subtitle).text = + resources.getText(R.string.controls_favorite_subtitle) + iconView = requireViewById(com.android.internal.R.id.icon) + iconFrame = requireViewById(R.id.icon_frame) + structurePager = requireViewById<ViewPager2>(R.id.structure_pager) + structurePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + mTooltipManager?.hide(true) + } + }) + bindButtons() } - private fun setUpRecyclerView() { - val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin) - val itemDecorator = MarginItemDecorator(margin, margin) - val layoutInflater = LayoutInflater.from(applicationContext) - val elevation = resources.getFloat(R.dimen.control_card_elevation) - - adapterAll = ControlAdapter(layoutInflater, elevation) - recyclerViewAll = requireViewById<RecyclerView>(R.id.listAll).apply { - adapter = adapterAll - layoutManager = GridLayoutManager(applicationContext, 2).apply { - spanSizeLookup = adapterAll.spanSizeLookup + private fun bindButtons() { + requireViewById<Button>(R.id.other_apps).apply { + visibility = View.VISIBLE + setOnClickListener { + this@ControlsFavoritingActivity.onBackPressed() + } + } + + doneButton = requireViewById<Button>(R.id.done).apply { + isEnabled = false + setOnClickListener { + if (component == null) return@setOnClickListener + listOfStructures.forEach { + val favoritesForStorage = it.model.favorites.map { it.build() } + controller.replaceFavoritesForStructure( + StructureInfo(component!!, it.structureName, favoritesForStorage) + ) + } + finishAffinity() } - addItemDecoration(itemDecorator) } } + override fun onPause() { + super.onPause() + mTooltipManager?.hide(false) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + mTooltipManager?.hide(false) + } + override fun onDestroy() { currentUserTracker.stopTracking() + listingController.removeCallback(listingCallback) + controller.cancelLoad() super.onDestroy() } + + private fun shouldShowTooltip(): Boolean { + return Prefs.getInt(applicationContext, TOOLTIP_PREFS_KEY, 0) < TOOLTIP_MAX_SHOWN + } } + +data class StructureContainer(val structureName: CharSequence, val model: ControlsModel) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt index 9b108cf3e34b..94487e5a584a 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt @@ -88,6 +88,8 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( init { serviceListing.addCallback(serviceListingCallback) + serviceListing.setListening(true) + serviceListing.reload() } override fun changeUser(newUser: UserHandle) { @@ -95,11 +97,12 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( callbacks.clear() availableServices = emptyList() serviceListing.setListening(false) - serviceListing.removeCallback(serviceListingCallback) currentUserId = newUser.identifier val contextForUser = context.createContextAsUser(newUser, 0) serviceListing = serviceListingBuilder(contextForUser) serviceListing.addCallback(serviceListingCallback) + serviceListing.setListening(true) + serviceListing.reload() } } @@ -118,12 +121,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( backgroundExecutor.execute { Log.d(TAG, "Subscribing callback") callbacks.add(listener) - if (callbacks.size == 1) { - serviceListing.setListening(true) - serviceListing.reload() - } else { - listener.onServicesUpdated(getCurrentServices()) - } + listener.onServicesUpdated(getCurrentServices()) } } @@ -136,9 +134,6 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( backgroundExecutor.execute { Log.d(TAG, "Unsubscribing callback") callbacks.remove(listener) - if (callbacks.size == 0) { - serviceListing.setListening(false) - } } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt index 463632b319bb..a7fc2ac80070 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt @@ -21,7 +21,6 @@ import android.app.Dialog import android.content.ComponentName import android.content.DialogInterface import android.content.Intent -import android.graphics.drawable.Icon import android.os.Bundle import android.os.UserHandle import android.service.controls.Control @@ -137,11 +136,10 @@ class ControlsRequestDialog @Inject constructor( } fun createDialog(label: CharSequence): Dialog { - - val renderInfo = RenderInfo.lookup(control.deviceType, true) + val renderInfo = RenderInfo.lookup(this, component, control.deviceType, true) val frame = LayoutInflater.from(this).inflate(R.layout.controls_dialog, null).apply { requireViewById<ImageView>(R.id.icon).apply { - setImageIcon(Icon.createWithResource(context, renderInfo.iconResourceId)) + setImageDrawable(renderInfo.icon) setImageTintList( context.resources.getColorStateList(renderInfo.foreground, context.theme)) } @@ -176,4 +174,4 @@ class ControlsRequestDialog @Inject constructor( } finish() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt new file mode 100644 index 000000000000..72b10982e237 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt @@ -0,0 +1,52 @@ +/* + * 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.controls.management + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import com.android.systemui.qs.PageIndicator + +/** + * Page indicator for management screens. + * + * Adds RTL support to [PageIndicator]. To be used with [ViewPager2]. + */ +class ManagementPageIndicator( + context: Context, + attrs: AttributeSet +) : PageIndicator(context, attrs) { + + override fun setLocation(location: Float) { + // Location doesn't know about RTL + if (layoutDirection == View.LAYOUT_DIRECTION_RTL) { + val numPages = childCount + super.setLocation(numPages - 1 - location) + } else { + super.setLocation(location) + } + } + + var visibilityListener: (Int) -> Unit = {} + + override fun onVisibilityChanged(changedView: View, visibility: Int) { + super.onVisibilityChanged(changedView, visibility) + if (changedView == this) { + visibilityListener(visibility) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt new file mode 100644 index 000000000000..cb67454195ec --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt @@ -0,0 +1,73 @@ +/* + * 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.controls.management + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.android.systemui.R + +class StructureAdapter( + private val models: List<StructureContainer> +) : RecyclerView.Adapter<StructureAdapter.StructureHolder>() { + + override fun onCreateViewHolder(parent: ViewGroup, p1: Int): StructureHolder { + val layoutInflater = LayoutInflater.from(parent.context) + return StructureHolder( + layoutInflater.inflate(R.layout.controls_structure_page, parent, false) + ) + } + + override fun getItemCount() = models.size + + override fun onBindViewHolder(holder: StructureHolder, index: Int) { + holder.bind(models[index].model) + } + + class StructureHolder(view: View) : RecyclerView.ViewHolder(view) { + + private val recyclerView: RecyclerView + private val controlAdapter: ControlAdapter + + init { + recyclerView = itemView.requireViewById<RecyclerView>(R.id.listAll) + val elevation = itemView.context.resources.getFloat(R.dimen.control_card_elevation) + controlAdapter = ControlAdapter(elevation) + setUpRecyclerView() + } + + fun bind(model: ControlsModel) { + controlAdapter.changeModel(model) + } + + private fun setUpRecyclerView() { + val margin = itemView.context.resources + .getDimensionPixelSize(R.dimen.controls_card_margin) + val itemDecorator = MarginItemDecorator(margin, margin) + + recyclerView.apply { + this.adapter = controlAdapter + layoutManager = GridLayoutManager(recyclerView.context, 2).apply { + spanSizeLookup = controlAdapter.spanSizeLookup + } + addItemDecoration(itemDecorator) + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt index eb84a8bcbfbd..680d0066fc56 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt @@ -20,6 +20,7 @@ import android.app.PendingIntent import android.content.Intent import android.provider.Settings import android.service.controls.actions.BooleanAction +import android.service.controls.actions.CommandAction import android.util.Log import android.view.HapticFeedbackConstants @@ -36,6 +37,10 @@ object ControlActionCoordinator { cvh.clipLayer.setLevel(nextLevel) } + fun touch(cvh: ControlViewHolder, templateId: String) { + cvh.action(CommandAction(templateId)) + } + fun longPress(cvh: ControlViewHolder) { // Long press snould only be called when there is valid control state, otherwise ignore cvh.cws.control?.let { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index b1b98bc7d044..fc5663fe8c97 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -19,11 +19,11 @@ package com.android.systemui.controls.ui import android.content.Context import android.graphics.BlendMode import android.graphics.drawable.ClipDrawable -import android.graphics.drawable.Icon import android.graphics.drawable.LayerDrawable import android.service.controls.Control import android.service.controls.actions.ControlAction import android.service.controls.templates.ControlTemplate +import android.service.controls.templates.StatelessTemplate import android.service.controls.templates.TemperatureControlTemplate import android.service.controls.templates.ToggleRangeTemplate import android.service.controls.templates.ToggleTemplate @@ -122,19 +122,25 @@ class ControlViewHolder( return when { status == Control.STATUS_UNKNOWN -> UnknownBehavior::class template is ToggleTemplate -> ToggleBehavior::class + template is StatelessTemplate -> TouchBehavior::class template is ToggleRangeTemplate -> ToggleRangeBehavior::class template is TemperatureControlTemplate -> TemperatureControlBehavior::class else -> DefaultBehavior::class } } - internal fun applyRenderInfo(ri: RenderInfo) { + internal fun applyRenderInfo(enabled: Boolean, offset: Int = 0) { + setEnabled(enabled) + + val deviceType = cws.control?.let { it.getDeviceType() } ?: cws.ci.deviceType + val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset) + val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) val bg = context.getResources().getColorStateList(ri.background, context.getTheme()) status.setTextColor(fg) statusExtra.setTextColor(fg) - icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId)) + icon.setImageDrawable(ri.icon) icon.setImageTintList(fg) clipLayer.getDrawable().apply { @@ -143,7 +149,7 @@ class ControlViewHolder( } } - fun setEnabled(enabled: Boolean) { + private fun setEnabled(enabled: Boolean) { status.setEnabled(enabled) icon.setEnabled(enabled) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index ca6619b59c4d..bde966ca067e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -16,19 +16,14 @@ package com.android.systemui.controls.ui -import android.accounts.Account -import android.accounts.AccountManager import android.app.Dialog import android.content.ComponentName import android.content.Context import android.content.Intent -import android.content.ServiceConnection import android.content.SharedPreferences import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable -import android.os.IBinder import android.service.controls.Control -import android.service.controls.TokenProvider import android.service.controls.actions.ControlAction import android.util.Log import android.view.ContextThemeWrapper @@ -61,68 +56,6 @@ import java.text.Collator import javax.inject.Inject import javax.inject.Singleton -// TEMP CODE for MOCK -private const val TOKEN = "https://www.googleapis.com/auth/assistant" -private const val SCOPE = "oauth2:" + TOKEN -private var tokenProviderConnection: TokenProviderConnection? = null -class TokenProviderConnection( - val cc: ControlsController, - val context: Context, - val structure: StructureInfo? -) : ServiceConnection { - private var mTokenProvider: TokenProvider? = null - - override fun onServiceConnected(cName: ComponentName, binder: IBinder) { - Thread({ - Log.i(ControlsUiController.TAG, "TokenProviderConnection connected") - mTokenProvider = TokenProvider.Stub.asInterface(binder) - - val mLastAccountName = mTokenProvider?.getAccountName() - - if (mLastAccountName == null || mLastAccountName.isEmpty()) { - Log.e(ControlsUiController.TAG, "NO ACCOUNT IS SET. Open HomeMock app") - } else { - mTokenProvider?.setAuthToken(getAuthToken(mLastAccountName)) - structure?.let { - cc.subscribeToFavorites(it) - } - } - }, "TokenProviderThread").start() - } - - override fun onServiceDisconnected(cName: ComponentName) { - mTokenProvider = null - } - - fun getAuthToken(accountName: String): String? { - val am = AccountManager.get(context) - val accounts = am.getAccountsByType("com.google") - if (accounts == null || accounts.size == 0) { - Log.w(ControlsUiController.TAG, "No com.google accounts found") - return null - } - - var account: Account? = null - for (a in accounts) { - if (a.name.equals(accountName)) { - account = a - break - } - } - - if (account == null) { - account = accounts[0] - } - - try { - return am.blockingGetAuthToken(account!!, SCOPE, true) - } catch (e: Throwable) { - Log.e(ControlsUiController.TAG, "Error getting auth token", e) - return null - } - } -} - private data class ControlKey(val componentName: ComponentName, val controlId: String) @Singleton @@ -215,21 +148,10 @@ class ControlsUiControllerImpl @Inject constructor ( ControlKey(selectedStructure.componentName, it.ci.controlId) } listingCallback = createCallback(::showControlsView) + controlsController.get().subscribeToFavorites(selectedStructure) } controlsListingController.get().addCallback(listingCallback) - - // Temp code to pass auth - tokenProviderConnection = TokenProviderConnection(controlsController.get(), context, - selectedStructure) - - val serviceIntent = Intent() - serviceIntent.setComponent(ComponentName("com.android.systemui.home.mock", - "com.android.systemui.home.mock.AuthService")) - if (!context.bindService(serviceIntent, tokenProviderConnection!!, - Context.BIND_AUTO_CREATE)) { - controlsController.get().subscribeToFavorites(selectedStructure) - } } private fun showInitialSetupView(items: List<SelectionItem>) { @@ -266,36 +188,23 @@ class ControlsUiControllerImpl @Inject constructor ( parent.removeAllViews() controlViewsById.clear() - val inflater = LayoutInflater.from(context) - inflater.inflate(R.layout.controls_with_favorites, parent, true) + createListView() + createDropDown(items) + } - val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup - var lastRow: ViewGroup = createRow(inflater, listView) - selectedStructure.controls.forEach { - if (lastRow.getChildCount() == 2) { - lastRow = createRow(inflater, listView) - } - val item = inflater.inflate( - R.layout.controls_base_item, lastRow, false) as ViewGroup - lastRow.addView(item) - val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor, bgExecutor) - val key = ControlKey(selectedStructure.componentName, it.controlId) - cvh.bindData(controlsById.getValue(key)) - controlViewsById.put(key, cvh) + private fun createDropDown(items: List<SelectionItem>) { + items.forEach { + RenderInfo.registerComponentIcon(it.componentName, it.icon) } - // add spacer if necessary to keep control size consistent - if ((selectedStructure.controls.size % 2) == 1) { - lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f)) + val itemsByComponent = items.associateBy { it.componentName } + val itemsWithStructure = allStructures.mapNotNull { + itemsByComponent.get(it.componentName)?.copy(structure = it.structure) } + val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0] - val itemsByComponent = items.associateBy { it.componentName } var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply { - val listItems = allStructures.mapNotNull { - itemsByComponent.get(it.componentName)?.copy(structure = it.structure) - } - - addAll(listItems + addControlsItem) + addAll(itemsWithStructure + addControlsItem) } /* @@ -303,16 +212,15 @@ class ControlsUiControllerImpl @Inject constructor ( * for this dialog. Use a textView with the ListPopupWindow to achieve * a similar effect */ - val item = adapter.findSelectionItem(selectedStructure) ?: adapter.getItem(0) parent.requireViewById<TextView>(R.id.app_or_structure_spinner).apply { - setText(item.getTitle()) + setText(selectionItem.getTitle()) // override the default color on the dropdown drawable (getBackground() as LayerDrawable).getDrawable(1) .setTint(context.resources.getColor(R.color.control_spinner_dropdown, null)) } parent.requireViewById<ImageView>(R.id.app_icon).apply { - setContentDescription(item.getTitle()) - setImageDrawable(item.icon) + setContentDescription(selectionItem.getTitle()) + setImageDrawable(selectionItem.icon) } val anchor = parent.requireViewById<ViewGroup>(R.id.controls_header) anchor.setOnClickListener(object : View.OnClickListener { @@ -350,6 +258,36 @@ class ControlsUiControllerImpl @Inject constructor ( }) } + private fun createListView() { + val inflater = LayoutInflater.from(context) + inflater.inflate(R.layout.controls_with_favorites, parent, true) + + val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup + var lastRow: ViewGroup = createRow(inflater, listView) + selectedStructure.controls.forEach { + if (lastRow.getChildCount() == 2) { + lastRow = createRow(inflater, listView) + } + val baseLayout = inflater.inflate( + R.layout.controls_base_item, lastRow, false) as ViewGroup + lastRow.addView(baseLayout) + val cvh = ControlViewHolder( + baseLayout, + controlsController.get(), + uiExecutor, + bgExecutor + ) + val key = ControlKey(selectedStructure.componentName, it.controlId) + cvh.bindData(controlsById.getValue(key)) + controlViewsById.put(key, cvh) + } + + // add spacer if necessary to keep control size consistent + if ((selectedStructure.controls.size % 2) == 1) { + lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f)) + } + } + private fun loadPreference(structures: List<StructureInfo>): StructureInfo { if (structures.isEmpty()) return EMPTY_STRUCTURE @@ -393,13 +331,13 @@ class ControlsUiControllerImpl @Inject constructor ( activeDialog?.dismiss() controlsController.get().unsubscribe() - context.unbindService(tokenProviderConnection) - tokenProviderConnection = null parent.removeAllViews() controlsById.clear() controlViewsById.clear() controlsListingController.get().removeCallback(listingCallback) + + RenderInfo.clearCache() } override fun onRefreshState(componentName: ComponentName, controls: List<Control>) { @@ -438,6 +376,11 @@ class ControlsUiControllerImpl @Inject constructor ( listView.addView(row) return row } + + private fun findSelectionItem(si: StructureInfo, items: List<SelectionItem>): SelectionItem? = + items.firstOrNull { + it.componentName == si.componentName && it.structure == si.structure + } } private data class SelectionItem( @@ -468,17 +411,4 @@ private class ItemAdapter( } return view } - - fun findSelectionItem(si: StructureInfo): SelectionItem? { - var i = 0 - while (i < getCount()) { - val item = getItem(i) - if (item.componentName == si.componentName && - item.structure == si.structure) { - return item - } - i++ - } - return null - } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt index 17479293f7dc..e850a6a559d6 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt @@ -25,7 +25,6 @@ class DefaultBehavior : Behavior { override fun bind(cws: ControlWithState) { cvh.status.setText(cws.control?.getStatusText() ?: "") - cvh.setEnabled(false) - cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false)) + cvh.applyRenderInfo(false) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt index da52c6f8ee21..56267beb1b71 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt @@ -16,8 +16,14 @@ package com.android.systemui.controls.ui +import android.annotation.MainThread +import android.content.ComponentName +import android.content.Context +import android.graphics.drawable.Drawable import android.service.controls.DeviceTypes import android.service.controls.templates.TemperatureControlTemplate +import android.util.ArrayMap +import android.util.SparseArray import com.android.systemui.R @@ -31,18 +37,54 @@ data class IconState(val disabledResourceId: Int, val enabledResourceId: Int) { } } -data class RenderInfo(val iconResourceId: Int, val foreground: Int, val background: Int) { +data class RenderInfo(val icon: Drawable, val foreground: Int, val background: Int) { companion object { - fun lookup(deviceType: Int, enabled: Boolean): RenderInfo { - val iconState = deviceIconMap.getValue(deviceType) + const val APP_ICON_ID = -1 + private val iconMap = SparseArray<Drawable>() + private val appIconMap = ArrayMap<ComponentName, Drawable>() + + @MainThread + fun lookup( + context: Context, + componentName: ComponentName, + deviceType: Int, + enabled: Boolean, + offset: Int = 0 + ): RenderInfo { val (fg, bg) = deviceColorMap.getValue(deviceType) - return RenderInfo(iconState[enabled], fg, bg) + + val iconKey = if (offset > 0) { + deviceType * BUCKET_SIZE + offset + } else deviceType + + val iconState = deviceIconMap.getValue(iconKey) + val resourceId = iconState[enabled] + var icon: Drawable? = null + if (resourceId == APP_ICON_ID) { + icon = appIconMap.get(componentName) + if (icon == null) { + icon = context.resources + .getDrawable(R.drawable.ic_device_unknown_gm2_24px, null) + appIconMap.put(componentName, icon) + } + } else { + icon = iconMap.get(resourceId) + if (icon == null) { + icon = context.resources.getDrawable(resourceId, null) + iconMap.put(resourceId, icon) + } + } + return RenderInfo(icon!!, fg, bg) } - fun lookup(deviceType: Int, offset: Int, enabled: Boolean): RenderInfo { - val key = deviceType * BUCKET_SIZE + offset - return lookup(key, enabled) + fun registerComponentIcon(componentName: ComponentName, icon: Drawable) { + appIconMap.put(componentName, icon) + } + + fun clearCache() { + iconMap.clear() + appIconMap.clear() } } } @@ -116,6 +158,10 @@ private val deviceIconMap = mapOf<Int, IconState>( DeviceTypes.TYPE_MOP to IconState( R.drawable.ic_vacuum_gm2_24px, R.drawable.ic_vacuum_gm2_24px + ), + DeviceTypes.TYPE_ROUTINE to IconState( + RenderInfo.APP_ICON_ID, + RenderInfo.APP_ICON_ID ) ).withDefault { IconState( diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt index 239d2e5477bf..15c1dabf71bd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt @@ -47,10 +47,7 @@ class TemperatureControlBehavior : Behavior { val activeMode = template.getCurrentActiveMode() val enabled = activeMode != 0 && activeMode != TemperatureControlTemplate.MODE_OFF - val deviceType = control.getDeviceType() - clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL) - cvh.setEnabled(enabled) - cvh.applyRenderInfo(RenderInfo.lookup(deviceType, activeMode, enabled)) + cvh.applyRenderInfo(enabled, activeMode) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt index d306d7c84e7f..a3368ef77a56 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt @@ -34,7 +34,7 @@ class ToggleBehavior : Behavior { override fun initialize(cvh: ControlViewHolder) { this.cvh = cvh - cvh.setEnabled(false) + cvh.applyRenderInfo(false) cvh.layout.setOnClickListener(View.OnClickListener() { ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked()) @@ -51,10 +51,7 @@ class ToggleBehavior : Behavior { clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) val checked = template.isChecked() - val deviceType = control.getDeviceType() - clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL) - cvh.setEnabled(checked) - cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked)) + cvh.applyRenderInfo(checked) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index cca56c219972..6595b55a691d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -56,7 +56,7 @@ class ToggleRangeBehavior : Behavior { status = cvh.status context = status.getContext() - cvh.setEnabled(false) + cvh.applyRenderInfo(false) val gestureListener = ToggleRangeGestureListener(cvh.layout) val gestureDetector = GestureDetector(context, gestureListener) @@ -89,14 +89,11 @@ class ToggleRangeBehavior : Behavior { rangeTemplate = template.getRange() val checked = template.isChecked() - val deviceType = control.getDeviceType() - val currentRatio = rangeTemplate.getCurrentValue() / (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()) updateRange(currentRatio, checked) - cvh.setEnabled(checked) - cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked)) + cvh.applyRenderInfo(checked) } fun beginUpdateRange() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt new file mode 100644 index 000000000000..d64a5f060487 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt @@ -0,0 +1,58 @@ +/* + * 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.controls.ui + +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import android.view.View +import android.service.controls.Control +import android.service.controls.templates.StatelessTemplate + +import com.android.systemui.R +import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL + +/** + * Supports touch events, but has no notion of state as the {@link ToggleBehavior} does. Must be + * used with {@link StatelessTemplate}. + */ +class TouchBehavior : Behavior { + lateinit var clipLayer: Drawable + lateinit var template: StatelessTemplate + lateinit var control: Control + lateinit var cvh: ControlViewHolder + + override fun initialize(cvh: ControlViewHolder) { + this.cvh = cvh + cvh.applyRenderInfo(false) + + cvh.layout.setOnClickListener(View.OnClickListener() { + ControlActionCoordinator.touch(cvh, template.getTemplateId()) + }) + } + + override fun bind(cws: ControlWithState) { + this.control = cws.control!! + cvh.status.setText(control.getStatusText()) + template = control.getControlTemplate() as StatelessTemplate + + val ld = cvh.layout.getBackground() as LayerDrawable + clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) + clipLayer.setLevel(MIN_LEVEL) + + cvh.applyRenderInfo(false) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt index 1f33b8515dc7..c3572491f9f1 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt @@ -25,7 +25,6 @@ class UnknownBehavior : Behavior { override fun bind(cws: ControlWithState) { cvh.status.setText(cvh.context.getString(com.android.internal.R.string.loading)) - cvh.setEnabled(false) - cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false)) + cvh.applyRenderInfo(false) } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java index 2877ed045479..5b3d5c565472 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java @@ -44,8 +44,6 @@ import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BatteryControllerImpl; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.BluetoothControllerImpl; import com.android.systemui.statusbar.policy.CastController; @@ -179,12 +177,6 @@ public abstract class DependencyBinder { /** */ @Binds - public abstract BatteryController provideBatteryController( - BatteryControllerImpl controllerImpl); - - /** - */ - @Binds public abstract ManagedProfileController provideManagedProfileController( ManagedProfileControllerImpl controllerImpl); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 3a4b273e1c98..6c502d273a1c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -90,7 +90,7 @@ public class DependencyProvider { /** */ @Provides - public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) { + public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) { return new AmbientDisplayConfiguration(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index b4e51258763c..956b4aa177ea 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -43,6 +43,8 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.ShadeControllerImpl; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BatteryControllerImpl; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; @@ -78,6 +80,11 @@ public abstract class SystemUIDefaultModule { NotificationLockscreenUserManagerImpl notificationLockscreenUserManager); @Binds + @Singleton + public abstract BatteryController provideBatteryController( + BatteryControllerImpl controllerImpl); + + @Binds abstract DockManager bindDockManager(DockManagerImpl dockManager); @Binds diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index c28a719b9826..700a8611c8bd 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -49,6 +49,7 @@ import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.wakelock.WakeLock; import java.io.PrintWriter; +import java.util.Collection; import java.util.List; import java.util.function.Consumer; @@ -261,7 +262,7 @@ public class DozeSensors { private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @Override - public void onChange(boolean selfChange, Iterable<Uri> uris, int flags, int userId) { + public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) { if (userId != ActivityManager.getCurrentUser()) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 24f505d5a395..786ad2c7d82a 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1574,7 +1574,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private ControlsUiController mControlsUiController; private ViewGroup mControlsView; - private ViewGroup mContainerView; ActionsDialog(Context context, MyAdapter adapter, GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils, @@ -1671,7 +1670,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls); mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view); mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss()); - ((View) mGlobalActionsLayout.getParent()).setOnClickListener(view -> dismiss()); mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() { @Override public boolean dispatchPopulateAccessibilityEvent( @@ -1684,6 +1682,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mGlobalActionsLayout.setRotationListener(this::onRotate); mGlobalActionsLayout.setAdapter(mAdapter); + View globalActionsParent = (View) mGlobalActionsLayout.getParent(); + globalActionsParent.setOnClickListener(v -> dismiss()); + + // add fall-through dismiss handling to root view + View rootView = findViewById(com.android.systemui.R.id.global_actions_grid_root); + if (rootView != null) { + rootView.setOnClickListener(v -> dismiss()); + } + if (shouldUsePanel()) { initializePanel(); } @@ -1692,14 +1699,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA; } getWindow().setBackgroundDrawable(mBackgroundDrawable); - - if (mControlsView != null) { - mContainerView = findViewById(com.android.systemui.R.id.global_actions_container); - mContainerView.setOnTouchListener((v, e) -> { - dismiss(); - return true; - }); - } } private void fixNavBarClipping() { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java b/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java new file mode 100644 index 000000000000..622fa658f1b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java @@ -0,0 +1,43 @@ +/* + * 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.globalactions; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ScrollView; + +/** + * When measured, this view sets the minimum height of its first child to be equal to its own + * target height. + * + * This ensures fall-through click handlers can be placed on this view's child component. + */ +public class MinHeightScrollView extends ScrollView { + public MinHeightScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + View firstChild = getChildAt(0); + if (firstChild != null) { + firstChild.setMinimumHeight(MeasureSpec.getSize(heightMeasureSpec)); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 3933af00a47a..00b977e1d558 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -33,10 +33,10 @@ import android.app.PictureInPictureParams; import android.content.Context; import android.graphics.Rect; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; -import android.view.DisplayInfo; import android.view.ITaskOrganizer; import android.view.IWindowContainer; import android.view.SurfaceControl; @@ -47,7 +47,9 @@ import com.android.systemui.R; import com.android.systemui.pip.phone.PipUpdateThread; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; @@ -76,9 +78,9 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { private final PipBoundsHandler mPipBoundsHandler; private final PipAnimationController mPipAnimationController; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); - private final Rect mDisplayBounds = new Rect(); private final Rect mLastReportedBounds = new Rect(); private final int mCornerRadius; + private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>(); // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = @@ -201,29 +203,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } /** - * Updates the display dimension with given {@link DisplayInfo} - */ - @SuppressWarnings("unchecked") - public void onDisplayInfoChanged(DisplayInfo displayInfo) { - final Rect newDisplayBounds = new Rect(0, 0, - displayInfo.logicalWidth, displayInfo.logicalHeight); - if (!mDisplayBounds.equals(newDisplayBounds)) { - // Updates the exiting PiP animation in case the screen rotation changes in the middle. - // It's a legit case that PiP window is in portrait mode on home screen and - // the application requests landscape once back to fullscreen mode. - final PipAnimationController.PipTransitionAnimator animator = - mPipAnimationController.getCurrentAnimator(); - if (animator != null - && animator.getAnimationType() == ANIM_TYPE_BOUNDS - && animator.getDestinationBounds().equals(mDisplayBounds)) { - animator.updateEndValue(newDisplayBounds); - animator.setDestinationBounds(newDisplayBounds); - } - } - mDisplayBounds.set(newDisplayBounds); - } - - /** * Callback to issue the final {@link WindowContainerTransaction} on end of movements. * @param destinationBounds the final bounds. */ @@ -252,8 +231,9 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } catch (RemoteException e) { throw new RuntimeException("Unable to get leash", e); } + final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); + mBoundsToRestore.put(mToken.asBinder(), currentBounds); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { - final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); scheduleAnimateResizePip(currentBounds, destinationBounds, TRANSITION_DIRECTION_TO_PIP, DURATION_DEFAULT_MS, null); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { @@ -271,13 +251,15 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } @Override - public void taskVanished(IWindowContainer token) { + public void taskVanished(ActivityManager.RunningTaskInfo info) { + IWindowContainer token = info.token; Objects.requireNonNull(token, "Requires valid IWindowContainer"); if (token.asBinder() != mToken.asBinder()) { Log.wtf(TAG, "Unrecognized token: " + token); return; } - scheduleAnimateResizePip(mLastReportedBounds, mDisplayBounds, + final Rect boundsToRestore = mBoundsToRestore.remove(mToken.asBinder()); + scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore, TRANSITION_DIRECTION_TO_FULLSCREEN, DURATION_DEFAULT_MS, null); mInPip = false; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 4b97d134761e..1fdf92ee3832 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -128,7 +128,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } @Override - public void onPinnedActivityRestartAttempt(boolean clearedTask) { + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask) { + if (task.configuration.windowConfiguration.getWindowingMode() + != WINDOWING_MODE_PINNED) { + return; + } mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); } }; @@ -185,10 +190,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Override public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mHandler.post(() -> { - mPipBoundsHandler.onDisplayInfoChanged(displayInfo); - mPipTaskOrganizer.onDisplayInfoChanged(displayInfo); - }); + mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo)); } @Override @@ -352,7 +354,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, animatingBounds, fromImeAdjustment, fromShelfAdjustment, mTmpDisplayInfo.rotation); - mPipTaskOrganizer.onDisplayInfoChanged(mTmpDisplayInfo); } public void dump(PrintWriter pw) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index a5e9dbc41924..0c5a4d782e69 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -680,7 +680,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } @Override - public void onPinnedActivityRestartAttempt(boolean clearedTask) { + public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) { + if (task.configuration.windowConfiguration.getWindowingMode() + != WINDOWING_MODE_PINNED) { + return; + } if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()"); // If PIPed activity is launched again by Launcher or intent, make it fullscreen. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 17ac5e5ca47e..fab71918a3d1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -274,8 +274,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D try { tile = createTile(tileSpec); if (tile != null) { + tile.setTileSpec(tileSpec); if (tile.isAvailable()) { - tile.setTileSpec(tileSpec); newTiles.put(tileSpec, tile); mQSLogger.logTileAdded(tileSpec); } else { diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java index f3e2f104621e..5bf44c6a3003 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java @@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; + import android.annotation.Nullable; import android.app.ActivityManager; import android.app.trust.TrustManager; @@ -37,6 +39,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.phone.StatusBar; @@ -63,6 +66,21 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation { private TrustManager mTrustManager; private OverviewProxyService mOverviewProxyService; + private TaskStackChangeListener mListener = new TaskStackChangeListener() { + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask) { + if (task.configuration.windowConfiguration.getWindowingMode() + != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + return; + } + + if (homeTaskVisible) { + showRecentApps(false /* triggeredFromAltTab */); + } + } + }; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy, @@ -77,6 +95,7 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation { mHandler = new Handler(); mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); mOverviewProxyService = Dependency.get(OverviewProxyService.class); + ActivityManagerWrapper.getInstance().registerTaskStackListener(mListener); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index c6eecf260dac..ea5ec0503067 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -129,217 +129,221 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } }; - private DisplayImeController.ImePositionProcessor mImePositionProcessor = - new DisplayImeController.ImePositionProcessor() { - /** - * These are the y positions of the top of the IME surface when it is hidden and - * when it is shown respectively. These are NOT necessarily the top of the visible - * IME itself. - */ - private int mHiddenTop = 0; - private int mShownTop = 0; - - // The following are target states (what we are curretly animating towards). - /** - * {@code true} if, at the end of the animation, the split task positions should be - * adjusted by height of the IME. This happens when the secondary split is the IME - * target. - */ - private boolean mTargetAdjusted = false; - /** - * {@code true} if, at the end of the animation, the IME should be shown/visible - * regardless of what has focus. - */ - private boolean mTargetShown = false; - - // The following are the current (most recent) states set during animation - /** - * {@code true} if the secondary split has IME focus. - */ - private boolean mSecondaryHasFocus = false; - /** The dimming currently applied to the primary/secondary splits. */ - private float mLastPrimaryDim = 0.f; - private float mLastSecondaryDim = 0.f; - /** The most recent y position of the top of the IME surface */ - private int mLastAdjustTop = -1; - - // The following are states reached last time an animation fully completed. - /** {@code true} if the IME was shown/visible by the last-completed animation. */ - private boolean mImeWasShown = false; - /** - * {@code true} if the split positions were adjusted by the last-completed - * animation. - */ - private boolean mAdjusted = false; - - /** - * When some aspect of split-screen needs to animate independent from the IME, - * this will be non-null and control split animation. - */ - @Nullable - private ValueAnimator mAnimation = null; - - private boolean getSecondaryHasFocus(int displayId) { - try { - IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController() - .getImeTarget(displayId); - return imeSplit != null - && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder()); - } catch (RemoteException e) { - Slog.w(TAG, "Failed to get IME target", e); - } - return false; - } + private class DividerImeController implements DisplayImeController.ImePositionProcessor { + /** + * These are the y positions of the top of the IME surface when it is hidden and when it is + * shown respectively. These are NOT necessarily the top of the visible IME itself. + */ + private int mHiddenTop = 0; + private int mShownTop = 0; + + // The following are target states (what we are curretly animating towards). + /** + * {@code true} if, at the end of the animation, the split task positions should be + * adjusted by height of the IME. This happens when the secondary split is the IME target. + */ + private boolean mTargetAdjusted = false; + /** + * {@code true} if, at the end of the animation, the IME should be shown/visible + * regardless of what has focus. + */ + private boolean mTargetShown = false; + private float mTargetPrimaryDim = 0.f; + private float mTargetSecondaryDim = 0.f; + + // The following are the current (most recent) states set during animation + /** {@code true} if the secondary split has IME focus. */ + private boolean mSecondaryHasFocus = false; + /** The dimming currently applied to the primary/secondary splits. */ + private float mLastPrimaryDim = 0.f; + private float mLastSecondaryDim = 0.f; + /** The most recent y position of the top of the IME surface */ + private int mLastAdjustTop = -1; + + // The following are states reached last time an animation fully completed. + /** {@code true} if the IME was shown/visible by the last-completed animation. */ + private boolean mImeWasShown = false; + /** {@code true} if the split positions were adjusted by the last-completed animation. */ + private boolean mAdjusted = false; + + /** + * When some aspect of split-screen needs to animate independent from the IME, + * this will be non-null and control split animation. + */ + @Nullable + private ValueAnimator mAnimation = null; + + private boolean getSecondaryHasFocus(int displayId) { + try { + IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController() + .getImeTarget(displayId); + return imeSplit != null + && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder()); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to get IME target", e); + } + return false; + } - @Override - public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, - boolean imeShouldShow, SurfaceControl.Transaction t) { - mSecondaryHasFocus = getSecondaryHasFocus(displayId); - mTargetAdjusted = imeShouldShow && mSecondaryHasFocus - && !mSplitLayout.mDisplayLayout.isLandscape(); - mHiddenTop = hiddenTop; - mShownTop = shownTop; - mTargetShown = imeShouldShow; - if (mLastAdjustTop < 0) { - mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop; - } - if (mAnimation != null || (mImeWasShown && imeShouldShow - && mTargetAdjusted != mAdjusted)) { - // We need to animate adjustment independently of the IME position, so - // start our own animation to drive adjustment. This happens when a - // different split's editor has gained focus while the IME is still visible. - startAsyncAnimation(); - } - // Reposition the server's secondary split position so that it evaluates - // insets properly. - WindowContainerTransaction wct = new WindowContainerTransaction(); - if (mTargetAdjusted) { - mSplitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop); - wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary); - // "Freeze" the configuration size so that the app doesn't get a config - // or relaunch. This is required because normally nav-bar contributes - // to configuration bounds (via nondecorframe). - Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration - .windowConfiguration.getAppBounds()); - adjustAppBounds.offset(0, mSplitLayout.mAdjustedSecondary.top - - mSplitLayout.mSecondary.top); - wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds); - wct.setScreenSizeDp(mSplits.mSecondary.token, - mSplits.mSecondary.configuration.screenWidthDp, - mSplits.mSecondary.configuration.screenHeightDp); - } else { - wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary); - wct.setAppBounds(mSplits.mSecondary.token, null); - wct.setScreenSizeDp(mSplits.mSecondary.token, - SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); - } - try { - ActivityTaskManager.getTaskOrganizerController() - .applyContainerTransaction(wct, null /* organizer */); - } catch (RemoteException e) { - } - - // Update all the adjusted-for-ime states - mView.setAdjustedForIme(mTargetShown, mTargetShown - ? DisplayImeController.ANIMATION_DURATION_SHOW_MS - : DisplayImeController.ANIMATION_DURATION_HIDE_MS); - setAdjustedForIme(mTargetShown); - } + @Override + public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean imeShouldShow, SurfaceControl.Transaction t) { + if (!inSplitMode()) { + return; + } + final boolean splitIsVisible = !mView.isHidden(); + mSecondaryHasFocus = getSecondaryHasFocus(displayId); + mTargetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus + && !mSplitLayout.mDisplayLayout.isLandscape(); + mHiddenTop = hiddenTop; + mShownTop = shownTop; + mTargetShown = imeShouldShow; + if (mLastAdjustTop < 0) { + mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop; + } + mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible) + ? ADJUSTED_NONFOCUS_DIM : 0.f; + mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible) + ? ADJUSTED_NONFOCUS_DIM : 0.f; + if (mAnimation != null || (mImeWasShown && imeShouldShow + && mTargetAdjusted != mAdjusted)) { + // We need to animate adjustment independently of the IME position, so + // start our own animation to drive adjustment. This happens when a + // different split's editor has gained focus while the IME is still visible. + startAsyncAnimation(); + } + if (splitIsVisible) { + // If split is hidden, we don't want to trigger any relayouts that would cause the + // divider to show again. + updateImeAdjustState(); + } + } - @Override - public void onImePositionChanged(int displayId, int imeTop, - SurfaceControl.Transaction t) { - if (mAnimation != null) { - // Not synchronized with IME anymore, so return. - return; - } - final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop); - final float progress = mTargetShown ? fraction : 1.f - fraction; - onProgress(progress, t); - } + private void updateImeAdjustState() { + // Reposition the server's secondary split position so that it evaluates + // insets properly. + WindowContainerTransaction wct = new WindowContainerTransaction(); + if (mTargetAdjusted) { + mSplitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop); + wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary); + // "Freeze" the configuration size so that the app doesn't get a config + // or relaunch. This is required because normally nav-bar contributes + // to configuration bounds (via nondecorframe). + Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration + .windowConfiguration.getAppBounds()); + adjustAppBounds.offset(0, mSplitLayout.mAdjustedSecondary.top + - mSplitLayout.mSecondary.top); + wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds); + wct.setScreenSizeDp(mSplits.mSecondary.token, + mSplits.mSecondary.configuration.screenWidthDp, + mSplits.mSecondary.configuration.screenHeightDp); + } else { + wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary); + wct.setAppBounds(mSplits.mSecondary.token, null); + wct.setScreenSizeDp(mSplits.mSecondary.token, + SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); + } + try { + ActivityTaskManager.getTaskOrganizerController() + .applyContainerTransaction(wct, null /* organizer */); + } catch (RemoteException e) { + } - @Override - public void onImeEndPositioning(int displayId, boolean cancelled, - SurfaceControl.Transaction t) { - if (mAnimation != null) { - // Not synchronized with IME anymore, so return. - return; - } - onEnd(cancelled, t); - } + // Update all the adjusted-for-ime states + mView.setAdjustedForIme(mTargetShown, mTargetShown + ? DisplayImeController.ANIMATION_DURATION_SHOW_MS + : DisplayImeController.ANIMATION_DURATION_HIDE_MS); + setAdjustedForIme(mTargetShown); + } - private void onProgress(float progress, SurfaceControl.Transaction t) { - if (mTargetAdjusted != mAdjusted) { - final float fraction = mTargetAdjusted ? progress : 1.f - progress; - mLastAdjustTop = - (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop); - mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop); - mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary, - mSplitLayout.mAdjustedSecondary); - } - final float invProg = 1.f - progress; - final float targetPrimaryDim = (mSecondaryHasFocus && mTargetShown) - ? ADJUSTED_NONFOCUS_DIM : 0.f; - final float targetSecondaryDim = (!mSecondaryHasFocus && mTargetShown) - ? ADJUSTED_NONFOCUS_DIM : 0.f; - mView.setResizeDimLayer(t, true /* primary */, - mLastPrimaryDim * invProg + progress * targetPrimaryDim); - mView.setResizeDimLayer(t, false /* primary */, - mLastSecondaryDim * invProg + progress * targetSecondaryDim); - } + @Override + public void onImePositionChanged(int displayId, int imeTop, + SurfaceControl.Transaction t) { + if (mAnimation != null || !inSplitMode()) { + // Not synchronized with IME anymore, so return. + return; + } + final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop); + final float progress = mTargetShown ? fraction : 1.f - fraction; + onProgress(progress, t); + } - private void onEnd(boolean cancelled, SurfaceControl.Transaction t) { - if (!cancelled) { - onProgress(1.f, t); - mAdjusted = mTargetAdjusted; - mImeWasShown = mTargetShown; - mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop; - mLastPrimaryDim = - (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f; - mLastSecondaryDim = - (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f; - } - } + @Override + public void onImeEndPositioning(int displayId, boolean cancelled, + SurfaceControl.Transaction t) { + if (mAnimation != null || !inSplitMode()) { + // Not synchronized with IME anymore, so return. + return; + } + onEnd(cancelled, t); + } + + private void onProgress(float progress, SurfaceControl.Transaction t) { + if (mTargetAdjusted != mAdjusted) { + final float fraction = mTargetAdjusted ? progress : 1.f - progress; + mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop); + mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop); + mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary, + mSplitLayout.mAdjustedSecondary); + } + final float invProg = 1.f - progress; + mView.setResizeDimLayer(t, true /* primary */, + mLastPrimaryDim * invProg + progress * mTargetPrimaryDim); + mView.setResizeDimLayer(t, false /* primary */, + mLastSecondaryDim * invProg + progress * mTargetSecondaryDim); + } - private void startAsyncAnimation() { - if (mAnimation != null) { - mAnimation.cancel(); - } - mAnimation = ValueAnimator.ofFloat(0.f, 1.f); - mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS); - if (mTargetAdjusted != mAdjusted) { - final float fraction = - ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop); - final float progress = mTargetAdjusted ? fraction : 1.f - fraction; - mAnimation.setCurrentFraction(progress); - } - - mAnimation.addUpdateListener(animation -> { - SurfaceControl.Transaction t = mTransactionPool.acquire(); - float value = (float) animation.getAnimatedValue(); - onProgress(value, t); - t.apply(); - mTransactionPool.release(t); - }); - mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR); - mAnimation.addListener(new AnimatorListenerAdapter() { - private boolean mCancel = false; - @Override - public void onAnimationCancel(Animator animation) { - mCancel = true; - } - @Override - public void onAnimationEnd(Animator animation) { - SurfaceControl.Transaction t = mTransactionPool.acquire(); - onEnd(mCancel, t); - t.apply(); - mTransactionPool.release(t); - mAnimation = null; - } - }); - mAnimation.start(); + private void onEnd(boolean cancelled, SurfaceControl.Transaction t) { + if (!cancelled) { + onProgress(1.f, t); + mAdjusted = mTargetAdjusted; + mImeWasShown = mTargetShown; + mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop; + mLastPrimaryDim = mTargetPrimaryDim; + mLastSecondaryDim = mTargetSecondaryDim; + } + } + + private void startAsyncAnimation() { + if (mAnimation != null) { + mAnimation.cancel(); + } + mAnimation = ValueAnimator.ofFloat(0.f, 1.f); + mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS); + if (mTargetAdjusted != mAdjusted) { + final float fraction = + ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop); + final float progress = mTargetAdjusted ? fraction : 1.f - fraction; + mAnimation.setCurrentFraction(progress); + } + + mAnimation.addUpdateListener(animation -> { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + float value = (float) animation.getAnimatedValue(); + onProgress(value, t); + t.apply(); + mTransactionPool.release(t); + }); + mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR); + mAnimation.addListener(new AnimatorListenerAdapter() { + private boolean mCancel = false; + @Override + public void onAnimationCancel(Animator animation) { + mCancel = true; } - }; + @Override + public void onAnimationEnd(Animator animation) { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + onEnd(mCancel, t); + t.apply(); + mTransactionPool.release(t); + mAnimation = null; + } + }); + mAnimation.start(); + } + } + private final DividerImeController mImePositionProcessor = new DividerImeController(); public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy, DisplayController displayController, SystemWindows systemWindows, @@ -513,42 +517,37 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } } - private void setHomeStackResizable(boolean resizable) { - if (mHomeStackResizable == resizable) { - return; + /** Switch to minimized state if appropriate */ + public void setMinimized(final boolean minimized) { + mHandler.post(() -> { + setHomeMinimized(minimized, mHomeStackResizable); + }); + } + + private void setHomeMinimized(final boolean minimized, boolean homeStackResizable) { + WindowContainerTransaction wct = new WindowContainerTransaction(); + // Update minimized state + if (mMinimized != minimized) { + mMinimized = minimized; } - mHomeStackResizable = resizable; - if (!inSplitMode()) { - return; + // Always set this because we could be entering split when mMinimized is already true + wct.setFocusable(mSplits.mPrimary.token, !mMinimized); + + // Update home-stack resizability + if (mHomeStackResizable != homeStackResizable) { + mHomeStackResizable = homeStackResizable; + if (inSplitMode()) { + WindowManagerProxy.applyHomeTasksMinimized( + mSplitLayout, mSplits.mSecondary.token, wct); + } } - WindowManagerProxy.applyHomeTasksMinimized(mSplitLayout, mSplits.mSecondary.token); - } - private void updateMinimizedDockedStack(final boolean minimized, final long animDuration, - final boolean isHomeStackResizable) { - setHomeStackResizable(isHomeStackResizable); - if (animDuration > 0) { - mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable); - } else { - mView.setMinimizedDockStack(minimized, isHomeStackResizable); + // Sync state to DividerView if it exists. + if (mView != null) { + mView.setMinimizedDockStack(minimized, getAnimDuration(), homeStackResizable); } updateTouchable(); - } - - /** Switch to minimized state if appropriate */ - public void setMinimized(final boolean minimized) { - mHandler.post(() -> { - if (!inSplitMode()) { - return; - } - if (mMinimized == minimized) { - return; - } - mMinimized = minimized; - WindowManagerProxy.applyPrimaryFocusable(mSplits, !mMinimized); - mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable); - updateTouchable(); - }); + WindowManagerProxy.applyContainerTransaction(wct); } void setAdjustedForIme(boolean adjustedForIme) { @@ -646,46 +645,24 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } void ensureMinimizedSplit() { - final boolean wasMinimized = mMinimized; - mMinimized = true; - setHomeStackResizable(mSplits.mSecondary.isResizable()); - WindowManagerProxy.applyPrimaryFocusable(mSplits, false /* focusable */); + setHomeMinimized(true /* minimized */, mSplits.mSecondary.isResizable()); if (!inSplitMode()) { // Wasn't in split-mode yet, so enter now. if (DEBUG) { Log.d(TAG, " entering split mode with minimized=true"); } updateVisibility(true /* visible */); - } else if (!wasMinimized) { - if (DEBUG) { - Log.d(TAG, " in split mode, but minimizing "); - } - // Was already in split-mode, update just minimized state. - updateMinimizedDockedStack(mMinimized, getAnimDuration(), - mHomeStackResizable); } } void ensureNormalSplit() { - if (mMinimized) { - WindowManagerProxy.applyPrimaryFocusable(mSplits, true /* focusable */); - } + setHomeMinimized(false /* minimized */, mHomeStackResizable); if (!inSplitMode()) { // Wasn't in split-mode, so enter now. if (DEBUG) { Log.d(TAG, " enter split mode unminimized "); } - mMinimized = false; updateVisibility(true /* visible */); } - if (mMinimized) { - // Was in minimized state, so leave that. - if (DEBUG) { - Log.d(TAG, " in split mode already, but unminimizing "); - } - mMinimized = false; - updateMinimizedDockedStack(mMinimized, getAnimDuration(), - mHomeStackResizable); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 477cbb7c7ad0..4114bb9d055d 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -165,6 +165,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, // The view is removed or in the process of been removed from the system. private boolean mRemoved; + // Whether the surface for this view has been hidden regardless of actual visibility. This is + // used interact with keyguard. + private boolean mSurfaceHidden = false; + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -414,6 +418,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, /** Unlike setVisible, this directly hides the surface without changing view visibility. */ void setHidden(boolean hidden) { + if (mSurfaceHidden == hidden) { + return; + } + mSurfaceHidden = hidden; post(() -> { final SurfaceControl sc = getWindowSurfaceControl(); if (sc == null) { @@ -430,6 +438,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, }); } + boolean isHidden() { + return mSurfaceHidden; + } + public boolean startDragging(boolean animate, boolean touching) { cancelFlingAnimation(); if (touching) { @@ -1071,7 +1083,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, void setResizeDimLayer(Transaction t, boolean primary, float alpha) { SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim; - if (alpha <= 0.f) { + if (alpha <= 0.001f) { t.hide(dim); } else { t.setAlpha(dim, alpha); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java index 3020a25dfa47..729df3887915 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java @@ -88,6 +88,9 @@ public class DividerWindowManager { } public void setTouchable(boolean touchable) { + if (mView == null) { + return; + } boolean changed = false; if (!touchable && (mLp.flags & FLAG_NOT_TOUCHABLE) == 0) { mLp.flags |= FLAG_NOT_TOUCHABLE; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java index 5cc87996269b..48ea4aef9968 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java @@ -88,7 +88,7 @@ class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub { } @Override - public void taskVanished(IWindowContainer container) { + public void taskVanished(RunningTaskInfo taskInfo) { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index 167c33abac6e..fea57a320d04 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.graphics.Rect; @@ -137,17 +138,13 @@ public class WindowManagerProxy { return resizable; } - static void applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent) { - applyHomeTasksMinimized(layout, parent, null /* transaction */); - } - /** * Assign a fixed override-bounds to home tasks that reflect their geometry while the primary * split is minimized. This actually "sticks out" of the secondary split area, but when in * minimized mode, the secondary split gets a 'negative' crop to expose it. */ static boolean applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent, - WindowContainerTransaction t) { + @NonNull WindowContainerTransaction wct) { // Resize the home/recents stacks to the larger minimized-state size final Rect homeBounds; final ArrayList<IWindowContainer> homeStacks = new ArrayList<>(); @@ -158,19 +155,9 @@ public class WindowManagerProxy { homeBounds = new Rect(0, 0, layout.mDisplayLayout.width(), layout.mDisplayLayout.height()); } - WindowContainerTransaction wct = t != null ? t : new WindowContainerTransaction(); for (int i = homeStacks.size() - 1; i >= 0; --i) { wct.setBounds(homeStacks.get(i), homeBounds); } - if (t != null) { - return isHomeResizable; - } - try { - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, - null /* organizer */); - } catch (RemoteException e) { - Log.e(TAG, "Failed to resize home stacks ", e); - } return isHomeResizable; } @@ -301,10 +288,8 @@ public class WindowManagerProxy { } } - static void applyPrimaryFocusable(SplitScreenTaskOrganizer splits, boolean focusable) { + static void applyContainerTransaction(WindowContainerTransaction wct) { try { - WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setFocusable(splits.mPrimary.token, focusable); ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, null /* organizer */); } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 53605e5a308a..7c061574f19c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -282,7 +282,7 @@ public class ActivityLaunchAnimator { .withCornerRadius(mCornerRadius) .withVisibility(true) .build(); - mSyncRtTransactionApplier.scheduleApply(params); + mSyncRtTransactionApplier.scheduleApply(true /* earlyWakeup */, params); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt index 88888d10e283..269a7a59f1b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.statusbar.notification.interruption +package com.android.systemui.statusbar.notification import android.content.Context import android.media.MediaMetadata @@ -24,7 +24,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java index b5725029450d..df21f0b21ec1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.interruption; +package com.android.systemui.statusbar.notification; import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP; @@ -27,9 +27,6 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -42,7 +39,7 @@ public class NotificationAlertingManager { private final NotificationRemoteInputManager mRemoteInputManager; private final VisualStabilityManager mVisualStabilityManager; private final StatusBarStateController mStatusBarStateController; - private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final NotificationListener mNotificationListener; private HeadsUpManager mHeadsUpManager; @@ -55,13 +52,13 @@ public class NotificationAlertingManager { NotificationRemoteInputManager remoteInputManager, VisualStabilityManager visualStabilityManager, StatusBarStateController statusBarStateController, - NotificationInterruptStateProvider notificationInterruptionStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationListener notificationListener, HeadsUpManager headsUpManager) { mRemoteInputManager = remoteInputManager; mVisualStabilityManager = visualStabilityManager; mStatusBarStateController = statusBarStateController; - mNotificationInterruptStateProvider = notificationInterruptionStateProvider; + mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; mNotificationListener = notificationListener; mHeadsUpManager = headsUpManager; @@ -97,7 +94,7 @@ public class NotificationAlertingManager { if (entry.getRow().getPrivateLayout().getHeadsUpChild() != null) { // Possible for shouldHeadsUp to change between the inflation starting and ending. // If it does and we no longer need to heads up, we should free the view. - if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) { + if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) { mHeadsUpManager.showNotification(entry); if (!mStatusBarStateController.isDozing()) { // Mark as seen immediately @@ -112,7 +109,7 @@ public class NotificationAlertingManager { private void updateAlertState(NotificationEntry entry) { boolean alertAgain = alertAgain(entry, entry.getSbn().getNotification()); // includes check for whether this notification should be filtered: - boolean shouldAlert = mNotificationInterruptStateProvider.shouldHeadsUp(entry); + boolean shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry); final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.getKey()); if (wasAlerting) { if (shouldAlert) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java index 46d50441c06b..bbf2dde80040 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java @@ -14,35 +14,33 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.interruption; +package com.android.systemui.statusbar.notification; import static com.android.systemui.statusbar.StatusBarState.SHADE; import android.app.NotificationManager; -import android.content.ContentResolver; +import android.content.Context; import android.database.ContentObserver; import android.hardware.display.AmbientDisplayConfiguration; -import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; +import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.Dependency; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.HeadsUpManager; -import java.util.ArrayList; -import java.util.List; - import javax.inject.Inject; import javax.inject.Singleton; @@ -50,84 +48,120 @@ import javax.inject.Singleton; * Provides heads-up and pulsing state for notification entries. */ @Singleton -public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider { +public class NotificationInterruptionStateProvider { + private static final String TAG = "InterruptionStateProvider"; - private static final boolean DEBUG = true; //false; + private static final boolean DEBUG = false; private static final boolean DEBUG_HEADS_UP = true; private static final boolean ENABLE_HEADS_UP = true; private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; - private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>(); private final StatusBarStateController mStatusBarStateController; private final NotificationFilter mNotificationFilter; - private final ContentResolver mContentResolver; + private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; + + private final Context mContext; private final PowerManager mPowerManager; private final IDreamManager mDreamManager; - private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; private final BatteryController mBatteryController; - private final ContentObserver mHeadsUpObserver; + + private NotificationPresenter mPresenter; private HeadsUpManager mHeadsUpManager; + private HeadsUpSuppressor mHeadsUpSuppressor; + private ContentObserver mHeadsUpObserver; @VisibleForTesting protected boolean mUseHeadsUp = false; + private boolean mDisableNotificationAlerts; @Inject - public NotificationInterruptStateProviderImpl( - ContentResolver contentResolver, + public NotificationInterruptionStateProvider(Context context, NotificationFilter filter, + StatusBarStateController stateController, BatteryController batteryController) { + this(context, + (PowerManager) context.getSystemService(Context.POWER_SERVICE), + IDreamManager.Stub.asInterface( + ServiceManager.checkService(DreamService.DREAM_SERVICE)), + new AmbientDisplayConfiguration(context), + filter, + batteryController, + stateController); + } + + @VisibleForTesting + protected NotificationInterruptionStateProvider( + Context context, PowerManager powerManager, IDreamManager dreamManager, AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter notificationFilter, BatteryController batteryController, - StatusBarStateController statusBarStateController, - HeadsUpManager headsUpManager, - @Main Handler mainHandler) { - mContentResolver = contentResolver; + StatusBarStateController statusBarStateController) { + mContext = context; mPowerManager = powerManager; mDreamManager = dreamManager; mBatteryController = batteryController; mAmbientDisplayConfiguration = ambientDisplayConfiguration; mNotificationFilter = notificationFilter; mStatusBarStateController = statusBarStateController; - mHeadsUpManager = headsUpManager; - mHeadsUpObserver = new ContentObserver(mainHandler) { - @Override - public void onChange(boolean selfChange) { - boolean wasUsing = mUseHeadsUp; - mUseHeadsUp = ENABLE_HEADS_UP - && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( - mContentResolver, - Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, - Settings.Global.HEADS_UP_OFF); - Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); - if (wasUsing != mUseHeadsUp) { - if (!mUseHeadsUp) { - Log.d(TAG, "dismissing any existing heads up notification on " - + "disable event"); - mHeadsUpManager.releaseAllImmediately(); + } + + /** Sets up late-binding dependencies for this component. */ + public void setUpWithPresenter( + NotificationPresenter notificationPresenter, + HeadsUpManager headsUpManager, + HeadsUpSuppressor headsUpSuppressor) { + setUpWithPresenter(notificationPresenter, headsUpManager, headsUpSuppressor, + new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) { + @Override + public void onChange(boolean selfChange) { + boolean wasUsing = mUseHeadsUp; + mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts + && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, + Settings.Global.HEADS_UP_OFF); + Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); + if (wasUsing != mUseHeadsUp) { + if (!mUseHeadsUp) { + Log.d(TAG, + "dismissing any existing heads up notification on disable" + + " event"); + mHeadsUpManager.releaseAllImmediately(); + } + } } - } - } - }; + }); + } + + /** Sets up late-binding dependencies for this component. */ + public void setUpWithPresenter( + NotificationPresenter notificationPresenter, + HeadsUpManager headsUpManager, + HeadsUpSuppressor headsUpSuppressor, + ContentObserver observer) { + mPresenter = notificationPresenter; + mHeadsUpManager = headsUpManager; + mHeadsUpSuppressor = headsUpSuppressor; + mHeadsUpObserver = observer; if (ENABLE_HEADS_UP) { - mContentResolver.registerContentObserver( + mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true, mHeadsUpObserver); - mContentResolver.registerContentObserver( + mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, mHeadsUpObserver); } mHeadsUpObserver.onChange(true); // set up } - @Override - public void addSuppressor(NotificationInterruptSuppressor suppressor) { - mSuppressors.add(suppressor); - } - - @Override + /** + * Whether the notification should appear as a bubble with a fly-out on top of the screen. + * + * @param entry the entry to check + * @return true if the entry should bubble up, false otherwise + */ public boolean shouldBubbleUp(NotificationEntry entry) { final StatusBarNotification sbn = entry.getSbn(); @@ -167,8 +201,12 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return true; } - - @Override + /** + * Whether the notification should peek in from the top and alert the user. + * + * @param entry the entry to check + * @return true if the entry should heads up, false otherwise + */ public boolean shouldHeadsUp(NotificationEntry entry) { if (mStatusBarStateController.isDozing()) { return shouldHeadsUpWhenDozing(entry); @@ -177,17 +215,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter } } - /** - * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or - * incoming calls. - */ - @Override - public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { - return entry.getSbn().getNotification().fullScreenIntent != null - && (!shouldHeadsUp(entry) - || mStatusBarStateController.getState() == StatusBarState.KEYGUARD); - } - private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); @@ -244,15 +271,13 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } - for (int i = 0; i < mSuppressors.size(); i++) { - if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) { - if (DEBUG_HEADS_UP) { - Log.d(TAG, "No heads up: aborted by suppressor: " - + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey()); - } - return false; + if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) { + if (DEBUG_HEADS_UP) { + Log.d(TAG, "No heads up: aborted by suppressor: " + sbn.getKey()); } + return false; } + return true; } @@ -300,7 +325,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter } return false; } - return true; + return true; } /** @@ -309,7 +334,8 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter * @param entry the entry to check * @return true if these checks pass, false if the notification should not alert */ - private boolean canAlertCommon(NotificationEntry entry) { + @VisibleForTesting + public boolean canAlertCommon(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); if (mNotificationFilter.shouldFilterOut(entry)) { @@ -326,16 +352,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter } return false; } - - for (int i = 0; i < mSuppressors.size(); i++) { - if (mSuppressors.get(i).suppressInterruptions(entry)) { - if (DEBUG_HEADS_UP) { - Log.d(TAG, "No alerting: aborted by suppressor: " - + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey()); - } - return false; - } - } return true; } @@ -345,17 +361,15 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter * @param entry the entry to check * @return true if these checks pass, false if the notification should not alert */ - private boolean canAlertAwakeCommon(NotificationEntry entry) { + @VisibleForTesting + public boolean canAlertAwakeCommon(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); - for (int i = 0; i < mSuppressors.size(); i++) { - if (mSuppressors.get(i).suppressAwakeInterruptions(entry)) { - if (DEBUG_HEADS_UP) { - Log.d(TAG, "No alerting: aborted by suppressor: " - + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey()); - } - return false; + if (mPresenter.isDeviceInVrMode()) { + if (DEBUG_HEADS_UP) { + Log.d(TAG, "No alerting: no huns or vr mode"); } + return false; } if (isSnoozedPackage(sbn)) { @@ -378,4 +392,54 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter private boolean isSnoozedPackage(StatusBarNotification sbn) { return mHeadsUpManager.isSnoozed(sbn.getPackageName()); } + + /** Sets whether to disable all alerts. */ + public void setDisableNotificationAlerts(boolean disableNotificationAlerts) { + mDisableNotificationAlerts = disableNotificationAlerts; + mHeadsUpObserver.onChange(true); + } + + /** Whether all alerts are disabled. */ + @VisibleForTesting + public boolean areNotificationAlertsDisabled() { + return mDisableNotificationAlerts; + } + + /** Whether HUNs should be used. */ + @VisibleForTesting + public boolean getUseHeadsUp() { + return mUseHeadsUp; + } + + protected NotificationPresenter getPresenter() { + return mPresenter; + } + + /** + * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or + * incoming calls. + * + * @param entry the entry that was added + * @return {@code true} if we should launch the full screen intent + */ + public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { + return entry.getSbn().getNotification().fullScreenIntent != null + && (!shouldHeadsUp(entry) + || mStatusBarStateController.getState() == StatusBarState.KEYGUARD); + } + + /** A component which can suppress heads-up notifications due to the overall state of the UI. */ + public interface HeadsUpSuppressor { + /** + * Returns false if the provided notification is ineligible for heads-up according to this + * component. + * + * @param entry entry of the notification that might be heads upped + * @param sbn notification that might be heads upped + * @return false if the notification can not be heads upped + */ + boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn); + + } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 27476964b9af..8a23e3796e9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification import android.animation.ObjectAnimator +import android.content.Context import android.util.FloatProperty import com.android.systemui.Interpolators import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -25,10 +26,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.PanelExpansionListener -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import javax.inject.Inject @@ -36,14 +37,15 @@ import javax.inject.Singleton @Singleton class NotificationWakeUpCoordinator @Inject constructor( - private val mHeadsUpManager: HeadsUpManager, - private val statusBarStateController: StatusBarStateController, - private val bypassController: KeyguardBypassController, - private val dozeParameters: DozeParameters -) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener { + private val mHeadsUpManagerPhone: HeadsUpManagerPhone, + private val statusBarStateController: StatusBarStateController, + private val bypassController: KeyguardBypassController, + private val dozeParameters: DozeParameters) + : OnHeadsUpChangedListener, StatusBarStateController.StateListener, + PanelExpansionListener { - private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>( - "notificationVisibility") { + private val mNotificationVisibility + = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") { override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) { coordinator.setVisibilityAmount(value) @@ -76,10 +78,10 @@ class NotificationWakeUpCoordinator @Inject constructor( field = value willWakeUp = false if (value) { - if (mNotificationsVisible && !mNotificationsVisibleForExpansion && - !bypassController.bypassEnabled) { + if (mNotificationsVisible && !mNotificationsVisibleForExpansion + && !bypassController.bypassEnabled) { // We're waking up while pulsing, let's make sure the animation looks nice - mStackScroller.wakeUpFromPulse() + mStackScroller.wakeUpFromPulse(); } if (bypassController.bypassEnabled && !mNotificationsVisible) { // Let's make sure our huns become visible once we are waking up in case @@ -98,7 +100,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } private var collapsedEnoughToHide: Boolean = false - lateinit var iconAreaController: NotificationIconAreaController + lateinit var iconAreaController : NotificationIconAreaController var pulsing: Boolean = false set(value) { @@ -130,8 +132,8 @@ class NotificationWakeUpCoordinator @Inject constructor( var canShow = pulsing if (bypassController.bypassEnabled) { // We also allow pulsing on the lock screen! - canShow = canShow || (wakingUp || willWakeUp || fullyAwake) && - statusBarStateController.state == StatusBarState.KEYGUARD + canShow = canShow || (wakingUp || willWakeUp || fullyAwake) + && statusBarStateController.state == StatusBarState.KEYGUARD // We want to hide the notifications when collapsed too much if (collapsedEnoughToHide) { canShow = false @@ -141,7 +143,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } init { - mHeadsUpManager.addListener(this) + mHeadsUpManagerPhone.addListener(this) statusBarStateController.addCallback(this) addListener(object : WakeUpListener { override fun onFullyHiddenChanged(isFullyHidden: Boolean) { @@ -153,7 +155,7 @@ class NotificationWakeUpCoordinator @Inject constructor( increaseSpeed = false) } } - }) + }); } fun setStackScroller(stackScroller: NotificationStackScrollLayout) { @@ -176,55 +178,46 @@ class NotificationWakeUpCoordinator @Inject constructor( * @param animate should this change be animated * @param increaseSpeed should the speed be increased of the animation */ - fun setNotificationsVisibleForExpansion( - visible: Boolean, - animate: Boolean, - increaseSpeed: Boolean - ) { + fun setNotificationsVisibleForExpansion(visible: Boolean, animate: Boolean, + increaseSpeed: Boolean) { mNotificationsVisibleForExpansion = visible updateNotificationVisibility(animate, increaseSpeed) if (!visible && mNotificationsVisible) { // If we stopped expanding and we're still visible because we had a pulse that hasn't // times out, let's release them all to make sure were not stuck in a state where // notifications are visible - mHeadsUpManager.releaseAllImmediately() + mHeadsUpManagerPhone.releaseAllImmediately() } } fun addListener(listener: WakeUpListener) { - wakeUpListeners.add(listener) + wakeUpListeners.add(listener); } fun removeListener(listener: WakeUpListener) { - wakeUpListeners.remove(listener) + wakeUpListeners.remove(listener); } - private fun updateNotificationVisibility( - animate: Boolean, - increaseSpeed: Boolean - ) { + private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) { // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore - var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications() + var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications() visible = visible && canShowPulsingHuns if (!visible && mNotificationsVisible && (wakingUp || willWakeUp) && mDozeAmount != 0.0f) { // let's not make notifications invisible while waking up, otherwise the animation // is strange - return + return; } setNotificationsVisible(visible, animate, increaseSpeed) } - private fun setNotificationsVisible( - visible: Boolean, - animate: Boolean, - increaseSpeed: Boolean - ) { + private fun setNotificationsVisible(visible: Boolean, animate: Boolean, + increaseSpeed: Boolean) { if (mNotificationsVisible == visible) { return } mNotificationsVisible = visible - mVisibilityAnimator?.cancel() + mVisibilityAnimator?.cancel(); if (animate) { notifyAnimationStart(visible) startVisibilityAnimation(increaseSpeed) @@ -237,8 +230,8 @@ class NotificationWakeUpCoordinator @Inject constructor( if (updateDozeAmountIfBypass()) { return } - if (linear != 1.0f && linear != 0.0f && - (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) { + if (linear != 1.0f && linear != 0.0f + && (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) { // Let's notify the scroller that an animation started notifyAnimationStart(mLinearDozeAmount == 1.0f) } @@ -252,17 +245,17 @@ class NotificationWakeUpCoordinator @Inject constructor( mStackScroller.setDozeAmount(mDozeAmount) updateHideAmount() if (changed && linear == 0.0f) { - setNotificationsVisible(visible = false, animate = false, increaseSpeed = false) + setNotificationsVisible(visible = false, animate = false, increaseSpeed = false); setNotificationsVisibleForExpansion(visible = false, animate = false, increaseSpeed = false) } } override fun onStateChanged(newState: Int) { - updateDozeAmountIfBypass() + updateDozeAmountIfBypass(); if (bypassController.bypassEnabled && - newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED && - (!statusBarStateController.isDozing || shouldAnimateVisibility())) { + newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED + && (!statusBarStateController.isDozing || shouldAnimateVisibility())) { // We're leaving shade locked. Let's animate the notifications away setNotificationsVisible(visible = true, increaseSpeed = false, animate = false) setNotificationsVisible(visible = false, increaseSpeed = false, animate = true) @@ -273,23 +266,23 @@ class NotificationWakeUpCoordinator @Inject constructor( override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) { val collapsedEnough = expansion <= 0.9f if (collapsedEnough != this.collapsedEnoughToHide) { - val couldShowPulsingHuns = canShowPulsingHuns + val couldShowPulsingHuns = canShowPulsingHuns; this.collapsedEnoughToHide = collapsedEnough if (couldShowPulsingHuns && !canShowPulsingHuns) { updateNotificationVisibility(animate = true, increaseSpeed = true) - mHeadsUpManager.releaseAllImmediately() + mHeadsUpManagerPhone.releaseAllImmediately() } } } private fun updateDozeAmountIfBypass(): Boolean { if (bypassController.bypassEnabled) { - var amount = 1.0f - if (statusBarStateController.state == StatusBarState.SHADE || - statusBarStateController.state == StatusBarState.SHADE_LOCKED) { - amount = 0.0f + var amount = 1.0f; + if (statusBarStateController.state == StatusBarState.SHADE + || statusBarStateController.state == StatusBarState.SHADE_LOCKED) { + amount = 0.0f; } - setDozeAmount(amount, amount) + setDozeAmount(amount, amount) return true } return false @@ -307,7 +300,7 @@ class NotificationWakeUpCoordinator @Inject constructor( visibilityAnimator.setInterpolator(Interpolators.LINEAR) var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong() if (increaseSpeed) { - duration = (duration.toFloat() / 1.5F).toLong() + duration = (duration.toFloat() / 1.5F).toLong(); } visibilityAnimator.setDuration(duration) visibilityAnimator.start() @@ -318,7 +311,7 @@ class NotificationWakeUpCoordinator @Inject constructor( mLinearVisibilityAmount = visibilityAmount mVisibilityAmount = mVisibilityInterpolator.getInterpolation( visibilityAmount) - handleAnimationFinished() + handleAnimationFinished(); updateHideAmount() } @@ -329,7 +322,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } } - fun getWakeUpHeight(): Float { + fun getWakeUpHeight() : Float { return mStackScroller.wakeUpHeight } @@ -337,7 +330,7 @@ class NotificationWakeUpCoordinator @Inject constructor( val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount) val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount) mStackScroller.setHideAmount(linearAmount, amount) - notificationsFullyHidden = linearAmount == 1.0f + notificationsFullyHidden = linearAmount == 1.0f; } private fun notifyAnimationStart(awake: Boolean) { @@ -368,7 +361,7 @@ class NotificationWakeUpCoordinator @Inject constructor( // if we animate, we see the shelf briefly visible. Instead we fully animate // the notification and its background out animate = false - } else if (!wakingUp && !willWakeUp) { + } else if (!wakingUp && !willWakeUp){ // TODO: look that this is done properly and not by anyone else entry.setHeadsUpAnimatingAway(true) mEntrySetToClearWhenFinished.add(entry) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 4beeedecfdf5..e8a62e48e75e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -37,8 +37,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.NotificationClicker; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; import com.android.systemui.statusbar.notification.row.NotifBindPipeline; @@ -66,7 +66,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private static final String TAG = "NotificationViewManager"; - private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final Context mContext; private final NotifBindPipeline mNotifBindPipeline; @@ -97,7 +97,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { StatusBarStateController statusBarStateController, NotificationGroupManager notificationGroupManager, NotificationGutsManager notificationGutsManager, - NotificationInterruptStateProvider notificationInterruptionStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, Provider<RowInflaterTask> rowInflaterTaskProvider, ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) { mContext = context; @@ -106,7 +106,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { mMessagingUtil = notificationMessagingUtil; mNotificationRemoteInputManager = notificationRemoteInputManager; mNotificationLockscreenUserManager = notificationLockscreenUserManager; - mNotificationInterruptStateProvider = notificationInterruptionStateProvider; + mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; mRowInflaterTaskProvider = rowInflaterTaskProvider; mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder; } @@ -243,7 +243,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); params.setUseLowPriority(entry.isAmbient()); - if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) { + if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) { params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP); } //TODO: Replace this API with RowContentBindParams directly diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index cd6affdaffac..e425ee951ba9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -31,8 +31,10 @@ import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; @@ -42,10 +44,9 @@ import com.android.systemui.statusbar.notification.collection.provider.HighPrior import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl; import com.android.systemui.statusbar.notification.init.NotificationsControllerStub; -import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; +import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -57,7 +58,6 @@ import java.util.concurrent.Executor; import javax.inject.Singleton; -import dagger.Binds; import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -127,7 +127,7 @@ public interface NotificationsModule { NotificationRemoteInputManager remoteInputManager, VisualStabilityManager visualStabilityManager, StatusBarStateController statusBarStateController, - NotificationInterruptStateProvider notificationInterruptStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationListener notificationListener, HeadsUpManager headsUpManager) { return new NotificationAlertingManager( @@ -135,7 +135,7 @@ public interface NotificationsModule { remoteInputManager, visualStabilityManager, statusBarStateController, - notificationInterruptStateProvider, + notificationInterruptionStateProvider, notificationListener, headsUpManager); } @@ -148,13 +148,22 @@ public interface NotificationsModule { @UiBackground Executor uiBgExecutor, NotificationEntryManager entryManager, StatusBarStateController statusBarStateController, - NotificationLogger.ExpansionStateLogger expansionStateLogger) { + NotificationLogger.ExpansionStateLogger expansionStateLogger, + NotificationPanelLogger notificationPanelLogger) { return new NotificationLogger( notificationListener, uiBgExecutor, entryManager, statusBarStateController, - expansionStateLogger); + expansionStateLogger, + notificationPanelLogger); + } + + /** Provides an instance of {@link NotificationPanelLogger} */ + @Singleton + @Provides + static NotificationPanelLogger provideNotificationPanelLogger() { + return new NotificationPanelLoggerImpl(); } /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */ @@ -201,9 +210,4 @@ public interface NotificationsModule { NotificationEntryManager entryManager) { return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager; } - - /** */ - @Binds - NotificationInterruptStateProvider bindNotificationInterruptStateProvider( - NotificationInterruptStateProviderImpl notificationInterruptStateProviderImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java deleted file mode 100644 index 3292a8fcdb50..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java +++ /dev/null @@ -1,59 +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.systemui.statusbar.notification.interruption; - -import com.android.systemui.statusbar.notification.collection.NotificationEntry; - -/** - * Provides bubble-up and heads-up state for notification entries. - * - * When a notification is heads-up when dozing, this is also called "pulsing." - */ -public interface NotificationInterruptStateProvider { - /** - * If the device is awake (not dozing): - * Whether the notification should peek in from the top and alert the user. - * - * If the device is dozing: - * Whether the notification should show the ambient view of the notification ("pulse"). - * - * @param entry the entry to check - * @return true if the entry should heads up, false otherwise - */ - boolean shouldHeadsUp(NotificationEntry entry); - - /** - * Whether the notification should appear as a bubble with a fly-out on top of the screen. - * - * @param entry the entry to check - * @return true if the entry should bubble up, false otherwise - */ - boolean shouldBubbleUp(NotificationEntry entry); - - /** - * Whether to launch the entry's full screen intent when the entry is added. - * - * @param entry the entry that was added - * @return {@code true} if we should launch the full screen intent - */ - boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry); - - /** - * Add a component that can suppress visual interruptions. - */ - void addSuppressor(NotificationInterruptSuppressor suppressor); -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java deleted file mode 100644 index c19f8bd1994a..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java +++ /dev/null @@ -1,64 +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.systemui.statusbar.notification.interruption; - -import com.android.systemui.statusbar.notification.collection.NotificationEntry; - -/** A component which can suppress visual interruptions of notifications such as heads-up and - * bubble-up. - */ -public interface NotificationInterruptSuppressor { - /** - * A unique name to identify this suppressor. - */ - default String getName() { - return this.getClass().getName(); - } - - /** - * Returns true if the provided notification is, when the device is awake, ineligible for - * heads-up according to this component. - * - * @param entry entry of the notification that might heads-up - * @return true if the heads up interruption should be suppressed when the device is awake - */ - default boolean suppressAwakeHeadsUp(NotificationEntry entry) { - return false; - } - - /** - * Returns true if the provided notification is, when the device is awake, ineligible for - * heads-up or bubble-up according to this component. - * - * @param entry entry of the notification that might heads-up or bubble-up - * @return true if interruptions should be suppressed when the device is awake - */ - default boolean suppressAwakeInterruptions(NotificationEntry entry) { - return false; - } - - /** - * Returns true if the provided notification is, regardless of awake/dozing state, - * ineligible for heads-up or bubble-up according to this component. - * - * @param entry entry of the notification that might heads-up or bubble-up - * @return true if interruptions should be suppressed - */ - default boolean suppressInterruptions(NotificationEntry entry) { - return false; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java index 6e161c9686dd..ad047889f29f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java @@ -70,6 +70,7 @@ public class NotificationLogger implements StateListener { private final NotificationListenerService mNotificationListener; private final Executor mUiBgExecutor; private final NotificationEntryManager mEntryManager; + private final NotificationPanelLogger mNotificationPanelLogger; private HeadsUpManager mHeadsUpManager; private final ExpansionStateLogger mExpansionStateLogger; @@ -198,13 +199,15 @@ public class NotificationLogger implements StateListener { @UiBackground Executor uiBgExecutor, NotificationEntryManager entryManager, StatusBarStateController statusBarStateController, - ExpansionStateLogger expansionStateLogger) { + ExpansionStateLogger expansionStateLogger, + NotificationPanelLogger notificationPanelLogger) { mNotificationListener = notificationListener; mUiBgExecutor = uiBgExecutor; mEntryManager = entryManager; mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mExpansionStateLogger = expansionStateLogger; + mNotificationPanelLogger = notificationPanelLogger; // Not expected to be destroyed, don't need to unsubscribe statusBarStateController.addCallback(this); @@ -264,6 +267,8 @@ public class NotificationLogger implements StateListener { // (Note that in cases where the scroller does emit events, this // additional event doesn't break anything.) mNotificationLocationsChangedListener.onChildLocationsChanged(); + mNotificationPanelLogger.logPanelShown(mListContainer.hasPulsingNotifications(), + mEntryManager.getVisibleNotifications()); } private void setDozing(boolean dozing) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java new file mode 100644 index 000000000000..9a25c480dfe8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java @@ -0,0 +1,95 @@ +/* + * 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.logging; + +import android.annotation.Nullable; +import android.service.notification.StatusBarNotification; + +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.logging.nano.Notifications; + +import java.util.List; +/** + * Statsd logging for notification panel. + */ +public interface NotificationPanelLogger { + + /** + * Log a NOTIFICATION_PANEL_REPORTED statsd event. + * @param visibleNotifications as provided by NotificationEntryManager.getVisibleNotifications() + */ + void logPanelShown(boolean isLockscreen, + @Nullable List<NotificationEntry> visibleNotifications); + + enum NotificationPanelEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Notification panel shown from status bar.") + NOTIFICATION_PANEL_OPEN_STATUS_BAR(200), + @UiEvent(doc = "Notification panel shown from lockscreen.") + NOTIFICATION_PANEL_OPEN_LOCKSCREEN(201); + + private final int mId; + NotificationPanelEvent(int id) { + mId = id; + } + @Override public int getId() { + return mId; + } + + public static NotificationPanelEvent fromLockscreen(boolean isLockscreen) { + return isLockscreen ? NOTIFICATION_PANEL_OPEN_LOCKSCREEN : + NOTIFICATION_PANEL_OPEN_STATUS_BAR; + } + } + + /** + * Composes a NotificationsList proto from the list of visible notifications. + * @param visibleNotifications as provided by NotificationEntryManager.getVisibleNotifications() + * @return NotificationList proto suitable for SysUiStatsLog.write(NOTIFICATION_PANEL_REPORTED) + */ + static Notifications.NotificationList toNotificationProto( + @Nullable List<NotificationEntry> visibleNotifications) { + Notifications.NotificationList notificationList = new Notifications.NotificationList(); + if (visibleNotifications == null) { + return notificationList; + } + final Notifications.Notification[] proto_array = + new Notifications.Notification[visibleNotifications.size()]; + int i = 0; + for (NotificationEntry ne : visibleNotifications) { + final StatusBarNotification n = ne.getSbn(); + if (n != null) { + final Notifications.Notification proto = new Notifications.Notification(); + proto.uid = n.getUid(); + proto.packageName = n.getPackageName(); + if (n.getInstanceId() != null) { + proto.instanceId = n.getInstanceId().getId(); + } + // TODO set np.groupInstanceId + if (n.getNotification() != null) { + proto.isGroupSummary = n.getNotification().isGroupSummary(); + } + proto.section = 1 + ne.getBucket(); // We want 0 to mean not set / unknown + proto_array[i] = proto; + } + ++i; + } + notificationList.notifications = proto_array; + return notificationList; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java new file mode 100644 index 000000000000..75a60194f2fa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java @@ -0,0 +1,41 @@ +/* + * 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.logging; + +import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.logging.nano.Notifications; + +import com.google.protobuf.nano.MessageNano; + +import java.util.List; + +/** + * Normal implementation of NotificationPanelLogger. + */ +public class NotificationPanelLoggerImpl implements NotificationPanelLogger { + @Override + public void logPanelShown(boolean isLockscreen, + List<NotificationEntry> visibleNotifications) { + final Notifications.NotificationList proto = NotificationPanelLogger.toNotificationProto( + visibleNotifications); + SysUiStatsLog.write(SysUiStatsLog.NOTIFICATION_PANEL_REPORTED, + /* int event_id */ NotificationPanelEvent.fromLockscreen(isLockscreen).getId(), + /* int num_notifications*/ proto.notifications.length, + /* byte[] notifications*/ MessageNano.toByteArray(proto)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto new file mode 100644 index 000000000000..552a5fb40a1c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto @@ -0,0 +1,49 @@ +/* + * 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.logging; + +/** + * NotificationList proto from atoms.proto, duplicated here so that it's accessible in the build. + * Must be kept in sync with the version in atoms.proto. + */ + +message Notification { + // The notifying app's uid and package. + optional int32 uid = 1; + optional string package_name = 2; + // A small system-assigned identifier for the notification. + optional int32 instance_id = 3; + + // Grouping information. + optional int32 group_instance_id = 4; + optional bool is_group_summary = 5; + + // The section of the shade that the notification is in. + // See NotificationSectionsManager.PriorityBucket. + enum NotificationSection { + SECTION_UNKNOWN = 0; + SECTION_HEADS_UP = 1; + SECTION_PEOPLE = 2; + SECTION_ALERTING = 3; + SECTION_SILENT = 4; + } + optional NotificationSection section = 6; +} + +message NotificationList { + repeated Notification notifications = 1; // An ordered sequence of notifications. +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 287ede48fa06..b3a62d8a4753 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -185,14 +185,15 @@ import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; +import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.init.NotificationsController; -import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -403,9 +404,10 @@ public class StatusBar extends SystemUI implements DemoMode, private final NotificationGutsManager mGutsManager; private final NotificationLogger mNotificationLogger; + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final NotificationViewHierarchyManager mViewHierarchyManager; private final KeyguardViewMediator mKeyguardViewMediator; - protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final NotificationAlertingManager mNotificationAlertingManager; // for disabling the status bar private int mDisabled1 = 0; @@ -619,9 +621,10 @@ public class StatusBar extends SystemUI implements DemoMode, RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, - NotificationInterruptStateProvider notificationInterruptStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, + NotificationAlertingManager notificationAlertingManager, DisplayMetrics displayMetrics, MetricsLogger metricsLogger, @UiBackground Executor uiBgExecutor, @@ -698,9 +701,10 @@ public class StatusBar extends SystemUI implements DemoMode, mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler; mGutsManager = notificationGutsManager; mNotificationLogger = notificationLogger; - mNotificationInterruptStateProvider = notificationInterruptStateProvider; + mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; mViewHierarchyManager = notificationViewHierarchyManager; mKeyguardViewMediator = keyguardViewMediator; + mNotificationAlertingManager = notificationAlertingManager; mDisplayMetrics = displayMetrics; mMetricsLogger = metricsLogger; mUiBgExecutor = uiBgExecutor; @@ -1234,9 +1238,9 @@ public class StatusBar extends SystemUI implements DemoMode, mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController, mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController, - mKeyguardStateController, mKeyguardIndicationController, - this /* statusBar */, mShadeController, mCommandQueue, mInitController, - mNotificationInterruptStateProvider); + mNotificationAlertingManager, mKeyguardStateController, + mKeyguardIndicationController, + this /* statusBar */, mShadeController, mCommandQueue, mInitController); mNotificationShelf.setOnActivatedListener(mPresenter); mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController); @@ -1585,9 +1589,8 @@ public class StatusBar extends SystemUI implements DemoMode, } if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { - if (areNotificationAlertsDisabled()) { - mHeadsUpManager.releaseAllImmediately(); - } + mNotificationInterruptionStateProvider.setDisableNotificationAlerts( + (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0); } if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) { @@ -1602,10 +1605,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - boolean areNotificationAlertsDisabled() { - return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; - } - protected H createHandler() { return new StatusBar.H(); } @@ -2333,7 +2332,7 @@ public class StatusBar extends SystemUI implements DemoMode, void checkBarModes() { if (mDemoMode) return; - if (mNotificationShadeWindowViewController != null) { + if (mNotificationShadeWindowViewController != null && getStatusBarTransitions() != null) { checkBarMode(mStatusBarMode, mStatusBarWindowState, getStatusBarTransitions()); } mNavigationBarController.checkNavBarModes(mDisplayId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 53fa2630a9c3..e1a20b6ac5d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -68,12 +68,12 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.HeadsUpUtil; @@ -108,7 +108,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotifCollection mNotifCollection; private final FeatureFlags mFeatureFlags; private final StatusBarStateController mStatusBarStateController; - private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final MetricsLogger mMetricsLogger; private final Context mContext; private final NotificationPanelViewController mNotificationPanel; @@ -142,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, StatusBar statusBar, KeyguardStateController keyguardStateController, - NotificationInterruptStateProvider notificationInterruptStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, Handler mainThreadHandler, Handler backgroundHandler, Executor uiBgExecutor, ActivityIntentHelper activityIntentHelper, BubbleController bubbleController, @@ -167,7 +167,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mActivityStarter = activityStarter; mEntryManager = entryManager; mStatusBarStateController = statusBarStateController; - mNotificationInterruptStateProvider = notificationInterruptStateProvider; + mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; mMetricsLogger = metricsLogger; mAssistManagerLazy = assistManagerLazy; mGroupManager = groupManager; @@ -436,7 +436,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } private void handleFullScreenIntent(NotificationEntry entry) { - if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { + if (mNotificationInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { if (shouldSuppressFullScreenIntent(entry)) { if (DEBUG) { Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.getKey()); @@ -603,7 +603,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final ActivityIntentHelper mActivityIntentHelper; private final BubbleController mBubbleController; private NotificationPanelViewController mNotificationPanelViewController; - private NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final ShadeController mShadeController; private NotificationPresenter mNotificationPresenter; private ActivityLaunchAnimator mActivityLaunchAnimator; @@ -626,7 +626,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit NotificationGroupManager groupManager, NotificationLockscreenUserManager lockscreenUserManager, KeyguardStateController keyguardStateController, - NotificationInterruptStateProvider notificationInterruptStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, @Main Handler mainThreadHandler, @@ -654,7 +654,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mGroupManager = groupManager; mLockscreenUserManager = lockscreenUserManager; mKeyguardStateController = keyguardStateController; - mNotificationInterruptStateProvider = notificationInterruptStateProvider; + mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; mMetricsLogger = metricsLogger; mLockPatternUtils = lockPatternUtils; mMainThreadHandler = mainThreadHandler; @@ -712,7 +712,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mShadeController, mStatusBar, mKeyguardStateController, - mNotificationInterruptStateProvider, + mNotificationInterruptionStateProvider, mMetricsLogger, mLockPatternUtils, mMainThreadHandler, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 79cea91b8612..30d6b5079166 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -60,13 +60,13 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -98,6 +98,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class); + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider = + Dependency.get(NotificationInterruptionStateProvider.class); private final NotificationMediaManager mMediaManager = Dependency.get(NotificationMediaManager.class); private final VisualStabilityManager mVisualStabilityManager = @@ -138,13 +140,13 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, ScrimController scrimController, ActivityLaunchAnimator activityLaunchAnimator, DynamicPrivacyController dynamicPrivacyController, + NotificationAlertingManager notificationAlertingManager, KeyguardStateController keyguardStateController, KeyguardIndicationController keyguardIndicationController, StatusBar statusBar, ShadeController shadeController, CommandQueue commandQueue, - InitController initController, - NotificationInterruptStateProvider notificationInterruptStateProvider) { + InitController initController) { mContext = context; mKeyguardStateController = keyguardStateController; mNotificationPanel = panel; @@ -214,7 +216,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mEntryManager.addNotificationLifetimeExtender(mGutsManager); mEntryManager.addNotificationLifetimeExtenders( remoteInputManager.getLifetimeExtenders()); - notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor); + mNotificationInterruptionStateProvider.setUpWithPresenter( + this, mHeadsUpManager, this::canHeadsUp); mLockscreenUserManager.setUpWithPresenter(this); mMediaManager.setUpWithPresenter(this); mVisualStabilityManager.setUpWithPresenter(this); @@ -333,6 +336,39 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, return mEntryManager.hasActiveNotifications(); } + public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) { + if (mStatusBar.isOccluded()) { + boolean devicePublic = mLockscreenUserManager. + isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); + boolean userPublic = devicePublic + || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId()); + boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry); + if (userPublic && needsRedaction) { + // TODO(b/135046837): we can probably relax this with dynamic privacy + return false; + } + } + + if (!mCommandQueue.panelsEnabled()) { + if (DEBUG) { + Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey()); + } + return false; + } + + if (sbn.getNotification().fullScreenIntent != null) { + if (mAccessibilityManager.isTouchExplorationEnabled()) { + if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey()); + return false; + } else { + // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent + return !mKeyguardStateController.isShowing() + || mStatusBar.isOccluded(); + } + } + return true; + } + @Override public void onUserSwitched(int newUserId) { // Begin old BaseStatusBar.userSwitched @@ -471,66 +507,4 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } } }; - - private final NotificationInterruptSuppressor mInterruptSuppressor = - new NotificationInterruptSuppressor() { - @Override - public String getName() { - return TAG; - } - - @Override - public boolean suppressAwakeHeadsUp(NotificationEntry entry) { - final StatusBarNotification sbn = entry.getSbn(); - if (mStatusBar.isOccluded()) { - boolean devicePublic = mLockscreenUserManager - .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); - boolean userPublic = devicePublic - || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId()); - boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry); - if (userPublic && needsRedaction) { - // TODO(b/135046837): we can probably relax this with dynamic privacy - return true; - } - } - - if (!mCommandQueue.panelsEnabled()) { - if (DEBUG) { - Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey()); - } - return true; - } - - if (sbn.getNotification().fullScreenIntent != null) { - // we don't allow head-up on the lockscreen (unless there's a - // "showWhenLocked" activity currently showing) if - // the potential HUN has a fullscreen intent - if (mKeyguardStateController.isShowing() && !mStatusBar.isOccluded()) { - if (DEBUG) { - Log.d(TAG, "No heads up: entry has fullscreen intent on lockscreen " - + sbn.getKey()); - } - return true; - } - - if (mAccessibilityManager.isTouchExplorationEnabled()) { - if (DEBUG) { - Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey()); - } - return true; - } - } - return false; - } - - @Override - public boolean suppressAwakeInterruptions(NotificationEntry entry) { - return isDeviceInVrMode(); - } - - @Override - public boolean suppressInterruptions(NotificationEntry entry) { - return mStatusBar.areNotificationAlertsDisabled(); - } - }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 824e0f0d4295..eec8d50f00de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -56,12 +56,13 @@ import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.init.NotificationsController; -import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -138,9 +139,10 @@ public interface StatusBarPhoneModule { RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, - NotificationInterruptStateProvider notificationInterruptStateProvider, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationViewHierarchyManager notificationViewHierarchyManager, KeyguardViewMediator keyguardViewMediator, + NotificationAlertingManager notificationAlertingManager, DisplayMetrics displayMetrics, MetricsLogger metricsLogger, @UiBackground Executor uiBgExecutor, @@ -216,9 +218,10 @@ public interface StatusBarPhoneModule { remoteInputQuickSettingsDisabler, notificationGutsManager, notificationLogger, - notificationInterruptStateProvider, + notificationInterruptionStateProvider, notificationViewHierarchyManager, keyguardViewMediator, + notificationAlertingManager, displayMetrics, metricsLogger, uiBgExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index cf9d8e1d85ec..24b96855377f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -53,6 +53,12 @@ public interface BatteryController extends DemoMode, Dumpable, boolean isAodPowerSave(); /** + * Set reverse state. + * @param isReverse true if turn on reverse, false otherwise + */ + default void setReverseState(boolean isReverse) {} + + /** * A listener that will be notified whenever a change in battery level or power save mode has * occurred. */ @@ -63,6 +69,9 @@ public interface BatteryController extends DemoMode, Dumpable, default void onPowerSaveChanged(boolean isPowerSave) { } + + default void onReverseChanged(boolean isReverse, int level, String name) { + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index d3e6f5309875..35954d88391a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -59,13 +59,13 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC private final EnhancedEstimates mEstimates; private final BroadcastDispatcher mBroadcastDispatcher; - private final ArrayList<BatteryController.BatteryStateChangeCallback> + protected final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>(); private final ArrayList<EstimateFetchCompletion> mFetchCallbacks = new ArrayList<>(); private final PowerManager mPowerManager; private final Handler mMainHandler; private final Handler mBgHandler; - private final Context mContext; + protected final Context mContext; private int mLevel; private boolean mPluggedIn; @@ -80,7 +80,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC @VisibleForTesting @Inject - BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates, + protected BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates, PowerManager powerManager, BroadcastDispatcher broadcastDispatcher, @Main Handler mainHandler, @Background Handler bgHandler) { mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index b84208c9ac35..99709402573c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -630,7 +630,7 @@ public class NetworkControllerImpl extends BroadcastReceiver @VisibleForTesting void doUpdateMobileControllers() { List<SubscriptionInfo> subscriptions = mSubscriptionManager - .getActiveAndHiddenSubscriptionInfoList(); + .getCompleteActiveSubscriptionInfoList(); if (subscriptions == null) { subscriptions = Collections.emptyList(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java index 74739e19ede9..73ffe42daac5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java @@ -43,7 +43,6 @@ import com.android.systemui.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; -import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Set; @@ -98,8 +97,27 @@ class AudioRecordingDisclosureBar { private TextView mTextView; @State private int mState = STATE_NOT_SHOWN; - private final Set<String> mAudioRecordingApps = new HashSet<>(); - private final Queue<String> mPendingNotifications = new LinkedList<>(); + /** + * Set of the applications that currently are conducting audio recording. + */ + private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>(); + /** + * Set of applications that we've notified the user about since the indicator came up. Meaning + * that if an application is in this list then at some point since the indicator came up, it + * was expanded showing this application's title. + * Used not to notify the user about the same application again while the indicator is shown. + * We empty this set every time the indicator goes off the screen (we always call {@code + * mSessionNotifiedPackages.clear()} before calling {@link #hide()}). + */ + private final Set<String> mSessionNotifiedPackages = new ArraySet<>(); + /** + * If an application starts recording while the TV indicator is neither in {@link + * #STATE_NOT_SHOWN} nor in {@link #STATE_MINIMIZED}, then we add the application's package + * name to the queue, from which we take packages names one by one to disclose the + * corresponding applications' titles to the user, whenever the indicator eventually comes to + * one of the two aforementioned states. + */ + private final Queue<String> mPendingNotificationPackages = new LinkedList<>(); AudioRecordingDisclosureBar(Context context) { mContext = context; @@ -116,10 +134,14 @@ class AudioRecordingDisclosureBar { } private void onStartedRecording(String packageName) { - if (!mAudioRecordingApps.add(packageName)) { + if (!mActiveAudioRecordingPackages.add(packageName)) { // This app is already known to perform recording return; } + if (!mSessionNotifiedPackages.add(packageName)) { + // We've already notified user about this app, no need to do it again. + return; + } switch (mState) { case STATE_NOT_SHOWN: @@ -137,13 +159,13 @@ class AudioRecordingDisclosureBar { case STATE_MINIMIZING: // Currently animating or expanded. Thus add to the pending notifications, and it // will be picked up once the indicator comes to the STATE_MINIMIZED. - mPendingNotifications.add(packageName); + mPendingNotificationPackages.add(packageName); break; } } private void onDoneRecording(String packageName) { - if (!mAudioRecordingApps.remove(packageName)) { + if (!mActiveAudioRecordingPackages.remove(packageName)) { // Was not marked as an active recorder, do nothing return; } @@ -151,7 +173,8 @@ class AudioRecordingDisclosureBar { // If not MINIMIZED, will check whether the indicator should be hidden when the indicator // comes to the STATE_MINIMIZED eventually. If is in the STATE_MINIMIZED, but there are // other active recorders - simply ignore. - if (mState == STATE_MINIMIZED && mAudioRecordingApps.isEmpty()) { + if (mState == STATE_MINIMIZED && mActiveAudioRecordingPackages.isEmpty()) { + mSessionNotifiedPackages.clear(); hide(); } } @@ -303,11 +326,12 @@ class AudioRecordingDisclosureBar { private void onMinimized() { mState = STATE_MINIMIZED; - if (!mPendingNotifications.isEmpty()) { + if (!mPendingNotificationPackages.isEmpty()) { // There is a new application that started recording, tell the user about it. - expand(mPendingNotifications.poll()); - } else if (mAudioRecordingApps.isEmpty()) { - // Nobody is recording anymore, remove the indicator. + expand(mPendingNotificationPackages.poll()); + } else if (mActiveAudioRecordingPackages.isEmpty()) { + // Nobody is recording anymore, clear state and remove the indicator. + mSessionNotifiedPackages.clear(); hide(); } } @@ -326,6 +350,12 @@ class AudioRecordingDisclosureBar { mBgRight = null; mState = STATE_NOT_SHOWN; + + // Check if anybody started recording while we were in STATE_DISAPPEARING + if (!mPendingNotificationPackages.isEmpty()) { + // There is a new application that started recording, tell the user about it. + show(mPendingNotificationPackages.poll()); + } } private void startPulsatingAnimation() { diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 30db37c4f128..f31f8eb0b20d 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -43,6 +43,7 @@ import com.google.android.collect.Sets; import org.json.JSONException; import org.json.JSONObject; +import java.util.Collection; import java.util.Map; import java.util.Set; @@ -101,7 +102,7 @@ public class ThemeOverlayController extends SystemUI { new ContentObserver(mBgHandler) { @Override - public void onChange(boolean selfChange, Iterable<Uri> uris, int flags, + public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) { if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId); if (ActivityManager.getCurrentUser() == userId) { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index b2a5f5bee543..248bdc870418 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -41,9 +41,9 @@ import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.util.leak.LeakDetector; -import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import javax.inject.Singleton; @@ -69,7 +69,8 @@ public class TunerServiceImpl extends TunerService { // Map of Uris we listen on to their settings keys. private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>(); // Map of settings keys to the listener. - private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>(); + private final ConcurrentHashMap<String, Set<Tunable>> mTunableLookup = + new ConcurrentHashMap<>(); // Set of all tunables, used for leak detection. private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null; private final Context mContext; @@ -262,7 +263,8 @@ public class TunerServiceImpl extends TunerService { } @Override - public void onChange(boolean selfChange, Iterable<Uri> uris, int flags, int userId) { + public void onChange(boolean selfChange, java.util.Collection<Uri> uris, + int flags, int userId) { if (userId == ActivityManager.getCurrentUser()) { for (Uri u : uris) { reloadSetting(u); diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt index 2276ba19e432..812a1e4bc121 100644 --- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt +++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt @@ -358,7 +358,7 @@ abstract class MagnetizedObject<T : Any>( targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf cancelAnimations() magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!) - animateStuckToTarget(targetObjectIsInMagneticFieldOf!!, velX, velY, false) + animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false) vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index ea6cf3329772..f7daf974b834 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -249,7 +249,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList()).thenReturn( + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn( new ArrayList<>()); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index eead1204aa11..4f4ce13e41ca 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -516,7 +516,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION); list.add(TEST_SUBSCRIPTION_2); - when(mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList()).thenReturn(list); + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list); mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged( TEST_SUBSCRIPTION_2.getSubscriptionId()); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java index 61991816a407..353fe625b8ea 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -50,6 +50,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; + @SmallTest @RunWith(AndroidTestingRunner.class) // Need to run tests on main looper because LiveData operations such as setData, observe, @@ -126,7 +128,7 @@ public final class ClockManagerTest extends SysuiTestCase { when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(null); when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(null); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the result is null, indicated the default clock face should be used. assertThat(mClockManager.getCurrentClock()).isNull(); } @@ -136,7 +138,7 @@ public final class ClockManagerTest extends SysuiTestCase { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the plugin is the bubble clock face. assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); } @@ -146,7 +148,7 @@ public final class ClockManagerTest extends SysuiTestCase { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the plugin is the bubble clock face. ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class); verify(mMockListener1).onClockChanged(captor.capture()); @@ -158,7 +160,7 @@ public final class ClockManagerTest extends SysuiTestCase { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the listeners receive separate instances of the Bubble clock plugin. ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class); ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class); @@ -175,7 +177,7 @@ public final class ClockManagerTest extends SysuiTestCase { // custom clock face. when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn("bad value"); // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); + mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); // THEN the result is null. assertThat(mClockManager.getCurrentClock()).isNull(); } 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 977d0bbd0004..78160c406964 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -45,11 +45,7 @@ import android.app.IActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.content.res.Resources; -import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.face.FaceManager; -import android.os.Handler; -import android.os.PowerManager; -import android.service.dreams.IDreamManager; import android.service.notification.ZenModeConfig; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -65,12 +61,14 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoveInterceptor; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -229,17 +227,15 @@ public class BubbleControllerTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); - TestableNotificationInterruptStateProviderImpl interruptionStateProvider = - new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), - mock(PowerManager.class), - mock(IDreamManager.class), - mock(AmbientDisplayConfiguration.class), + TestableNotificationInterruptionStateProvider interruptionStateProvider = + new TestableNotificationInterruptionStateProvider(mContext, mock(NotificationFilter.class), mock(StatusBarStateController.class), - mock(BatteryController.class), - mock(HeadsUpManager.class), - mock(Handler.class) - ); + mock(BatteryController.class)); + interruptionStateProvider.setUpWithPresenter( + mock(NotificationPresenter.class), + mock(HeadsUpManager.class), + mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class)); mBubbleData = new BubbleData(mContext); when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mBubbleController = new TestableBubbleController( @@ -403,7 +399,8 @@ public class BubbleControllerTest extends SysuiTestCase { // Switch which bubble is expanded mBubbleController.selectBubble(mRow.getEntry().getKey()); mBubbleData.setExpanded(true); - assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow.getEntry(), + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( mRow.getEntry())); @@ -496,21 +493,25 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow2.getEntry(), + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( mRow2.getEntry())); // Dismiss currently expanded - mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(), + mBubbleController.removeBubble( + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(), BubbleController.DISMISS_USER_GESTURE); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey()); // Make sure first bubble is selected - assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow.getEntry(), + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); // Dismiss that one - mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(), + mBubbleController.removeBubble( + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(), BubbleController.DISMISS_USER_GESTURE); // Make sure state changes and collapse happens 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 7fc83dac0280..5ef4cd251f11 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -41,11 +41,7 @@ import android.app.IActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.content.res.Resources; -import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.face.FaceManager; -import android.os.Handler; -import android.os.PowerManager; -import android.service.dreams.IDreamManager; import android.service.notification.ZenModeConfig; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -61,10 +57,12 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; @@ -132,19 +130,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private KeyguardBypassController mKeyguardBypassController; @Mock private FloatingContentCoordinator mFloatingContentCoordinator; - @Captor private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor; - private TestableBubbleController mBubbleController; private NotificationShadeWindowController mNotificationShadeWindowController; private NotifCollectionListener mEntryListener; - private NotificationTestHelper mNotificationTestHelper; private ExpandableNotificationRow mRow; private ExpandableNotificationRow mRow2; private ExpandableNotificationRow mNonBubbleNotifRow; - @Mock private BubbleController.BubbleStateChangeListener mBubbleStateChangeListener; @Mock @@ -218,17 +212,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); - TestableNotificationInterruptStateProviderImpl interruptionStateProvider = - new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), - mock(PowerManager.class), - mock(IDreamManager.class), - mock(AmbientDisplayConfiguration.class), + TestableNotificationInterruptionStateProvider interruptionStateProvider = + new TestableNotificationInterruptionStateProvider(mContext, mock(NotificationFilter.class), mock(StatusBarStateController.class), - mock(BatteryController.class), - mock(HeadsUpManager.class), - mock(Handler.class) - ); + mock(BatteryController.class)); + interruptionStateProvider.setUpWithPresenter( + mock(NotificationPresenter.class), + mock(HeadsUpManager.class), + mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class)); mBubbleData = new BubbleData(mContext); when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true); mBubbleController = new TestableBubbleController( @@ -313,7 +305,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { verify(mNotifCallback, times(1)).invalidateNotifications(anyString()); assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey())); mBubbleController.updateBubble(mRow2.getEntry()); - verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); + verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); @@ -383,7 +375,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Switch which bubble is expanded mBubbleController.selectBubble(mRow.getEntry().getKey()); mBubbleData.setExpanded(true); - assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow.getEntry(), + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( mRow.getEntry())); @@ -475,21 +468,25 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow2.getEntry(), + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( mRow2.getEntry())); // Dismiss currently expanded - mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(), + mBubbleController.removeBubble( + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(), BubbleController.DISMISS_USER_GESTURE); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey()); // Make sure first bubble is selected - assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow.getEntry(), + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); // Dismiss that one - mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(), + mBubbleController.removeBubble( + mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(), BubbleController.DISMISS_USER_GESTURE); // Make sure state changes and collapse happens @@ -649,7 +646,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { } @Test - public void removeBubble_intercepted() { + public void removeBubble_intercepted() { mEntryListener.onEntryAdded(mRow.getEntry()); mBubbleController.updateBubble(mRow.getEntry()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java index d3d90c408468..de1fb41ddbbd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java @@ -23,8 +23,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.ShadeController; @@ -44,7 +44,7 @@ public class TestableBubbleController extends BubbleController { ShadeController shadeController, BubbleData data, ConfigurationController configurationController, - NotificationInterruptStateProvider interruptionStateProvider, + NotificationInterruptionStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, NotificationGroupManager groupManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java deleted file mode 100644 index 17dc76b38a56..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java +++ /dev/null @@ -1,55 +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.systemui.bubbles; - -import android.content.ContentResolver; -import android.hardware.display.AmbientDisplayConfiguration; -import android.os.Handler; -import android.os.PowerManager; -import android.service.dreams.IDreamManager; - -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.NotificationFilter; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; -import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.HeadsUpManager; - -public class TestableNotificationInterruptStateProviderImpl - extends NotificationInterruptStateProviderImpl { - - TestableNotificationInterruptStateProviderImpl( - ContentResolver contentResolver, - PowerManager powerManager, - IDreamManager dreamManager, - AmbientDisplayConfiguration ambientDisplayConfiguration, - NotificationFilter filter, - StatusBarStateController statusBarStateController, - BatteryController batteryController, - HeadsUpManager headsUpManager, - Handler mainHandler) { - super(contentResolver, - powerManager, - dreamManager, - ambientDisplayConfiguration, - filter, - batteryController, - statusBarStateController, - headsUpManager, - mainHandler); - mUseHeadsUp = true; - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java new file mode 100644 index 000000000000..5d192b2071b5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java @@ -0,0 +1,35 @@ +/* + * 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.bubbles; + +import android.content.Context; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.policy.BatteryController; + +public class TestableNotificationInterruptionStateProvider + extends NotificationInterruptionStateProvider { + + TestableNotificationInterruptionStateProvider(Context context, + NotificationFilter filter, StatusBarStateController controller, + BatteryController batteryController) { + super(context, filter, controller, batteryController); + mUseHeadsUp = true; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt index eceb1ddaaf06..8cb1af46d307 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt @@ -22,6 +22,8 @@ import android.os.Binder import android.os.UserHandle import android.service.controls.Control import android.service.controls.DeviceTypes +import android.service.controls.IControlsSubscriber +import android.service.controls.IControlsSubscription import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -34,6 +36,8 @@ 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.Captor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` @@ -49,6 +53,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { companion object { fun <T> any(): T = Mockito.any<T>() + fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() private val TEST_COMPONENT_NAME_1 = ComponentName("TEST_PKG", "TEST_CLS_1") private val TEST_COMPONENT_NAME_2 = ComponentName("TEST_PKG", "TEST_CLS_2") private val TEST_COMPONENT_NAME_3 = ComponentName("TEST_PKG", "TEST_CLS_3") @@ -56,6 +61,8 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { @Mock private lateinit var mockControlsController: ControlsController + @Captor + private lateinit var subscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub> private val user = UserHandle.of(mContext.userId) private val otherUser = UserHandle.of(user.identifier + 1) @@ -97,6 +104,64 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { } @Test + fun testBindAndLoad_cancel() { + val callback = object : ControlsBindingController.LoadCallback { + override fun error(message: String) {} + + override fun accept(t: List<Control>) {} + } + val subscription = mock(IControlsSubscription::class.java) + + val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) + + verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor)) + subscriberCaptor.value.onSubscribe(Binder(), subscription) + + canceller.run() + verify(subscription).cancel() + } + + @Test + fun testBindAndLoad_noCancelAfterOnComplete() { + val callback = object : ControlsBindingController.LoadCallback { + override fun error(message: String) {} + + override fun accept(t: List<Control>) {} + } + val subscription = mock(IControlsSubscription::class.java) + + val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) + + verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor)) + val b = Binder() + subscriberCaptor.value.onSubscribe(b, subscription) + + subscriberCaptor.value.onComplete(b) + canceller.run() + verify(subscription, never()).cancel() + } + + @Test + fun testBindAndLoad_noCancelAfterOnError() { + val callback = object : ControlsBindingController.LoadCallback { + override fun error(message: String) {} + + override fun accept(t: List<Control>) {} + } + val subscription = mock(IControlsSubscription::class.java) + + val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) + + verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor)) + val b = Binder() + subscriberCaptor.value.onSubscribe(b, subscription) + + subscriberCaptor.value.onError(b, "") + canceller.run() + verify(subscription, never()).cancel() + } + + @Test fun testBindService() { controller.bindService(TEST_COMPONENT_NAME_1) executor.runAllReady() 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 183dde87bdf7..f9c98157edb8 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 @@ -234,7 +234,7 @@ class ControlsControllerImplTest : SysuiTestCase() { loaded = true assertEquals(1, controls.size) val controlStatus = controls[0] - assertEquals(ControlStatus(control, false), controlStatus) + assertEquals(ControlStatus(control, TEST_COMPONENT, false), controlStatus) assertTrue(favorites.isEmpty()) assertFalse(data.errorOnLoad) @@ -265,10 +265,10 @@ class ControlsControllerImplTest : SysuiTestCase() { loaded = true assertEquals(2, controls.size) val controlStatus = controls.first { it.control.controlId == TEST_CONTROL_ID } - assertEquals(ControlStatus(control, true), controlStatus) + assertEquals(ControlStatus(control, TEST_COMPONENT, true), controlStatus) val controlStatus2 = controls.first { it.control.controlId == TEST_CONTROL_ID_2 } - assertEquals(ControlStatus(control2, false), controlStatus2) + assertEquals(ControlStatus(control2, TEST_COMPONENT, false), controlStatus2) assertEquals(1, favorites.size) assertEquals(TEST_CONTROL_ID, favorites[0]) @@ -340,7 +340,91 @@ class ControlsControllerImplTest : SysuiTestCase() { controlLoadCallbackCaptor.value.error("") + delayableExecutor.runAllReady() + + assertTrue(loaded) + } + + @Test + fun testCancelLoad() { + val canceller = object : Runnable { + var ran = false + override fun run() { + ran = true + } + } + `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) + + var loaded = false + controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) + delayableExecutor.runAllReady() + controller.loadForComponent(TEST_COMPONENT, Consumer { + loaded = true + }) + + controller.cancelLoad() + delayableExecutor.runAllReady() + + assertFalse(loaded) + assertTrue(canceller.ran) + } + + @Test + fun testCancelLoad_noCancelAfterSuccessfulLoad() { + val canceller = object : Runnable { + var ran = false + override fun run() { + ran = true + } + } + `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) + + var loaded = false + controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) + delayableExecutor.runAllReady() + controller.loadForComponent(TEST_COMPONENT, Consumer { + loaded = true + }) + + verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), + capture(controlLoadCallbackCaptor)) + + controlLoadCallbackCaptor.value.accept(emptyList()) + + controller.cancelLoad() + delayableExecutor.runAllReady() + + assertTrue(loaded) + assertFalse(canceller.ran) + } + + @Test + fun testCancelLoad_noCancelAfterErrorLoad() { + val canceller = object : Runnable { + var ran = false + override fun run() { + ran = true + } + } + `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) + + var loaded = false + controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) + delayableExecutor.runAllReady() + controller.loadForComponent(TEST_COMPONENT, Consumer { + loaded = true + }) + + verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), + capture(controlLoadCallbackCaptor)) + + controlLoadCallbackCaptor.value.error("") + + controller.cancelLoad() + delayableExecutor.runAllReady() + assertTrue(loaded) + assertFalse(canceller.ran) } @Test @@ -519,7 +603,7 @@ class ControlsControllerImplTest : SysuiTestCase() { } @Test - fun testReplaceFavoritesForStructure_noFavorites() { + fun testReplaceFavoritesForStructure_noExistingFavorites() { controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) delayableExecutor.runAllReady() @@ -529,6 +613,16 @@ class ControlsControllerImplTest : SysuiTestCase() { } @Test + fun testReplaceFavoritesForStructure_doNotStoreEmptyStructure() { + controller.replaceFavoritesForStructure( + StructureInfo(TEST_COMPONENT, "Home", emptyList<ControlInfo>())) + delayableExecutor.runAllReady() + + assertEquals(0, controller.countFavoritesForComponent(TEST_COMPONENT)) + assertEquals(emptyList<ControlInfo>(), controller.getFavoritesForComponent(TEST_COMPONENT)) + } + + @Test fun testReplaceFavoritesForStructure_differentComponentsAreFilteredOut() { controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt index 68e1ec1d9f1d..133df2aa203f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.controls.management import android.app.PendingIntent +import android.content.ComponentName import android.service.controls.Control import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest @@ -78,6 +79,7 @@ class AllModelTest : SysuiTestCase() { Control.StatelessBuilder("$idPrefix$it", pendingIntent) .setZone(zoneMap(it)) .build(), + ComponentName("", ""), it in favoritesIndices ) } @@ -189,4 +191,4 @@ class AllModelTest : SysuiTestCase() { assertTrue(sameControl(it.first, it.second)) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt index 85e937e40acd..13a77083255c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt @@ -30,7 +30,6 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import org.junit.After import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -38,6 +37,7 @@ import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` +import org.mockito.Mockito.inOrder import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify @@ -98,74 +98,18 @@ class ControlsListingControllerImplTest : SysuiTestCase() { } @Test - fun testStartsOnUser() { - assertEquals(user, controller.currentUserId) - } - - @Test - fun testNoServices_notListening() { - assertTrue(controller.getCurrentServices().isEmpty()) - } - - @Test - fun testStartListening_onFirstCallback() { - controller.addCallback(mockCallback) - executor.runAllReady() - - verify(mockSL).setListening(true) - } - - @Test - fun testStartListening_onlyOnce() { - controller.addCallback(mockCallback) - controller.addCallback(mockCallbackOther) - - executor.runAllReady() - + fun testInitialStateListening() { verify(mockSL).setListening(true) + verify(mockSL).reload() } @Test - fun testStopListening_callbackRemoved() { - controller.addCallback(mockCallback) - - executor.runAllReady() - - controller.removeCallback(mockCallback) - - executor.runAllReady() - - verify(mockSL).setListening(false) - } - - @Test - fun testStopListening_notWhileRemainingCallbacks() { - controller.addCallback(mockCallback) - controller.addCallback(mockCallbackOther) - - executor.runAllReady() - - controller.removeCallback(mockCallback) - - executor.runAllReady() - - verify(mockSL, never()).setListening(false) - } - - @Test - fun testReloadOnFirstCallbackAdded() { - controller.addCallback(mockCallback) - executor.runAllReady() - - verify(mockSL).reload() + fun testStartsOnUser() { + assertEquals(user, controller.currentUserId) } @Test fun testCallbackCalledWhenAdded() { - `when`(mockSL.reload()).then { - serviceListingCallbackCaptor.value.onServicesReloaded(emptyList()) - } - controller.addCallback(mockCallback) executor.runAllReady() verify(mockCallback).onServicesUpdated(any()) @@ -209,5 +153,11 @@ class ControlsListingControllerImplTest : SysuiTestCase() { controller.changeUser(UserHandle.of(otherUser)) executor.runAllReady() assertEquals(otherUser, controller.currentUserId) + + val inOrder = inOrder(mockSL) + inOrder.verify(mockSL).setListening(false) + inOrder.verify(mockSL).addCallback(any()) // We add a callback because we replaced the SL + inOrder.verify(mockSL).setListening(true) + inOrder.verify(mockSL).reload() } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt index 9ffc29e0eb7e..c330b38fed42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.controls.management import android.app.PendingIntent +import android.content.ComponentName import android.service.controls.Control import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest @@ -70,6 +71,7 @@ open class FavoriteModelTest : SysuiTestCase() { Control.StatelessBuilder("$idPrefix$it", pendingIntent) .setZone((it % 3).toString()) .build(), + ComponentName("", ""), it in favoritesIndices ) } @@ -195,4 +197,4 @@ class FavoriteModelParameterizedTest(val from: Int, val to: Int) : FavoriteModel verifyNoMoreInteractions(allAdapter, favoritesAdapter) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 00980129cf74..73f3ddd29bcf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -22,7 +22,9 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertFalse; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -137,6 +139,8 @@ public class QSTileHostTest extends SysuiTestCase { return new TestTile1(mQSTileHost); } else if ("spec2".equals(spec)) { return new TestTile2(mQSTileHost); + } else if ("na".equals(spec)) { + return new NotAvailableTile(mQSTileHost); } else if (CUSTOM_TILE_SPEC.equals(spec)) { return mCustomTile; } else { @@ -283,6 +287,12 @@ public class QSTileHostTest extends SysuiTestCase { assertEquals(1, specs.size()); } + @Test + public void testNotAvailableTile_specNotNull() { + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "na"); + verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString()); + } + private static class TestQSTileHost extends QSTileHost { TestQSTileHost(Context context, StatusBarIconController iconController, QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper, @@ -369,4 +379,16 @@ public class QSTileHostTest extends SysuiTestCase { super(host); } } + + private class NotAvailableTile extends TestTile { + + protected NotAvailableTile(QSHost host) { + super(host); + } + + @Override + public boolean isAvailable() { + return false; + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java index f9c62e1ab604..1693e7f4df7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.notification.interruption; +package com.android.systemui.statusbar; import static android.app.Notification.FLAG_BUBBLE; @@ -30,14 +30,15 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.hardware.display.AmbientDisplayConfiguration; -import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.service.dreams.IDreamManager; @@ -49,6 +50,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.policy.BatteryController; @@ -66,7 +68,7 @@ import org.mockito.MockitoAnnotations; */ @RunWith(AndroidTestingRunner.class) @SmallTest -public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { +public class NotificationInterruptionStateProviderTest extends SysuiTestCase { @Mock PowerManager mPowerManager; @@ -79,36 +81,38 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Mock StatusBarStateController mStatusBarStateController; @Mock + NotificationPresenter mPresenter; + @Mock HeadsUpManager mHeadsUpManager; @Mock - BatteryController mBatteryController; + NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor; @Mock - Handler mMockHandler; + BatteryController mBatteryController; - private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider; + private NotificationInterruptionStateProvider mNotifInterruptionStateProvider; @Before public void setup() { MockitoAnnotations.initMocks(this); mNotifInterruptionStateProvider = - new NotificationInterruptStateProviderImpl( - mContext.getContentResolver(), + new TestableNotificationInterruptionStateProvider(mContext, mPowerManager, mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter, - mBatteryController, mStatusBarStateController, - mHeadsUpManager, - mMockHandler); + mBatteryController); - mNotifInterruptionStateProvider.mUseHeadsUp = true; + mNotifInterruptionStateProvider.setUpWithPresenter( + mPresenter, + mHeadsUpManager, + mHeadsUpSuppressor); } /** * Sets up the state such that any requests to - * {@link NotificationInterruptStateProviderImpl#canAlertCommon(NotificationEntry)} will + * {@link NotificationInterruptionStateProvider#canAlertCommon(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills group suppression check. */ private void ensureStateForAlertCommon() { @@ -117,16 +121,17 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { /** * Sets up the state such that any requests to - * {@link NotificationInterruptStateProviderImpl#canAlertAwakeCommon(NotificationEntry)} will + * {@link NotificationInterruptionStateProvider#canAlertAwakeCommon(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills launch fullscreen check. */ private void ensureStateForAlertAwakeCommon() { + when(mPresenter.isDeviceInVrMode()).thenReturn(false); when(mHeadsUpManager.isSnoozed(any())).thenReturn(false); } /** * Sets up the state such that any requests to - * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will + * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills importance & DND checks. */ private void ensureStateForHeadsUpWhenAwake() throws RemoteException { @@ -136,11 +141,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); when(mPowerManager.isScreenOn()).thenReturn(true); + when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); } /** * Sets up the state such that any requests to - * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will + * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills importance & DND checks. */ private void ensureStateForHeadsUpWhenDozing() { @@ -152,7 +158,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { /** * Sets up the state such that any requests to - * {@link NotificationInterruptStateProviderImpl#shouldBubbleUp(NotificationEntry)} will + * {@link NotificationInterruptionStateProvider#shouldBubbleUp(NotificationEntry)} will * pass as long its provided NotificationEntry fulfills importance & bubble checks. */ private void ensureStateForBubbleUp() { @@ -160,53 +166,75 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { ensureStateForAlertAwakeCommon(); } + /** + * Ensure that the disabled state is set correctly. + */ @Test - public void testDefaultSuppressorDoesNotSuppress() { - // GIVEN a suppressor without any overrides - final NotificationInterruptSuppressor defaultSuppressor = - new NotificationInterruptSuppressor() { - @Override - public String getName() { - return "defaultSuppressor"; - } - }; + public void testDisableNotificationAlerts() { + // Enabled by default + assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse(); - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + // Disable alerts + mNotifInterruptionStateProvider.setDisableNotificationAlerts(true); + assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isTrue(); - // THEN this suppressor doesn't suppress anything by default - assertThat(defaultSuppressor.suppressAwakeHeadsUp(entry)).isFalse(); - assertThat(defaultSuppressor.suppressAwakeInterruptions(entry)).isFalse(); - assertThat(defaultSuppressor.suppressInterruptions(entry)).isFalse(); + // Enable alerts + mNotifInterruptionStateProvider.setDisableNotificationAlerts(false); + assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse(); } + /** + * Ensure that the disabled alert state effects whether HUNs are enabled. + */ @Test - public void testShouldHeadsUpAwake() throws RemoteException { - ensureStateForHeadsUpWhenAwake(); + public void testHunSettingsChange_enabled_butAlertsDisabled() { + // Set up but without a mock change observer + mNotifInterruptionStateProvider.setUpWithPresenter( + mPresenter, + mHeadsUpManager, + mHeadsUpSuppressor); - NotificationEntry entry = createNotification(IMPORTANCE_HIGH); - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); + // HUNs enabled by default + assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isTrue(); + + // Set alerts disabled + mNotifInterruptionStateProvider.setDisableNotificationAlerts(true); + + // No more HUNs + assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse(); } + /** + * Alerts can happen. + */ @Test - public void testShouldNotHeadsUpAwake_flteredOut() throws RemoteException { - // GIVEN state for "heads up when awake" is true - ensureStateForHeadsUpWhenAwake(); + public void testCanAlertCommon_true() { + ensureStateForAlertCommon(); - // WHEN this entry should be filtered out - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); - when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true); + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isTrue(); + } - // THEN we shouldn't heads up this entry - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); + /** + * Filtered out notifications don't alert. + */ + @Test + public void testCanAlertCommon_false_filteredOut() { + ensureStateForAlertCommon(); + when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true); + + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse(); } + /** + * Grouped notifications have different alerting behaviours, sometimes the alert for a + * grouped notification may be suppressed {@link android.app.Notification#GROUP_ALERT_CHILDREN}. + */ @Test - public void testShouldNotHeadsUp_suppressedForGroups() throws RemoteException { - // GIVEN state for "heads up when awake" is true - ensureStateForHeadsUpWhenAwake(); + public void testCanAlertCommon_false_suppressedForGroups() { + ensureStateForAlertCommon(); - // WHEN the alert for a grouped notification is suppressed - // see {@link android.app.Notification#GROUP_ALERT_CHILDREN} NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") .setOpPkg("a") @@ -219,40 +247,40 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { .setImportance(IMPORTANCE_DEFAULT) .build(); - // THEN this entry shouldn't HUN - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); + assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse(); } + /** + * HUNs while dozing can happen. + */ @Test - public void testShouldHeadsUpWhenDozing() { + public void testShouldHeadsUpWhenDozing_true() { ensureStateForHeadsUpWhenDozing(); NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); } + /** + * Ambient display can show HUNs for new notifications, this may be disabled. + */ @Test - public void testShouldNotHeadsUpWhenDozing_pulseDisabled() { - // GIVEN state for "heads up when dozing" is true + public void testShouldHeadsUpWhenDozing_false_pulseDisabled() { ensureStateForHeadsUpWhenDozing(); - - // WHEN pulsing (HUNs when dozing) is disabled when(mAmbientDisplayConfiguration.pulseOnNotificationEnabled(anyInt())).thenReturn(false); - // THEN this entry shouldn't HUN NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } + /** + * If the device is not in ambient display or sleeping then we don't HUN. + */ @Test - public void testShouldNotHeadsUpWhenDozing_notDozing() { - // GIVEN state for "heads up when dozing" is true + public void testShouldHeadsUpWhenDozing_false_notDozing() { ensureStateForHeadsUpWhenDozing(); - - // WHEN we're not dozing (in ambient display or sleeping) when(mStatusBarStateController.isDozing()).thenReturn(false); - // THEN this entry shouldn't HUN NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } @@ -262,7 +290,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}. */ @Test - public void testShouldNotHeadsUpWhenDozing_suppressingAmbient() { + public void testShouldHeadsUpWhenDozing_false_suppressingAmbient() { ensureStateForHeadsUpWhenDozing(); NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); @@ -273,18 +301,23 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } + /** + * Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't + * get to pulse. + */ @Test - public void testShouldNotHeadsUpWhenDozing_lessImportant() { + public void testShouldHeadsUpWhenDozing_false_lessImportant() { ensureStateForHeadsUpWhenDozing(); - // Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't - // get to pulse NotificationEntry entry = createNotification(IMPORTANCE_LOW); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } + /** + * Heads up can happen. + */ @Test - public void testShouldHeadsUp() throws RemoteException { + public void testShouldHeadsUp_true() throws RemoteException { ensureStateForHeadsUpWhenAwake(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -292,11 +325,38 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } /** + * Heads up notifications can be disabled in general. + */ + @Test + public void testShouldHeadsUp_false_noHunsAllowed() throws RemoteException { + ensureStateForHeadsUpWhenAwake(); + + // Set alerts disabled, this should cause heads up to be false + mNotifInterruptionStateProvider.setDisableNotificationAlerts(true); + assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse(); + + NotificationEntry entry = createNotification(IMPORTANCE_HIGH); + assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); + } + + /** + * If the device is dozing, we don't show as heads up. + */ + @Test + public void testShouldHeadsUp_false_dozing() throws RemoteException { + ensureStateForHeadsUpWhenAwake(); + when(mStatusBarStateController.isDozing()).thenReturn(true); + + NotificationEntry entry = createNotification(IMPORTANCE_HIGH); + assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); + } + + /** * If the notification is a bubble, and the user is not on AOD / lockscreen, then * the bubble is shown rather than the heads up. */ @Test - public void testShouldNotHeadsUp_bubble() throws RemoteException { + public void testShouldHeadsUp_false_bubble() throws RemoteException { ensureStateForHeadsUpWhenAwake(); // Bubble bit only applies to interruption when we're in the shade @@ -309,7 +369,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { * If we're not allowed to alert in general, we shouldn't be shown as heads up. */ @Test - public void testShouldNotHeadsUp_filtered() throws RemoteException { + public void testShouldHeadsUp_false_alertCommonFalse() throws RemoteException { ensureStateForHeadsUpWhenAwake(); // Make canAlertCommon false by saying it's filtered out when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true); @@ -323,7 +383,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}. */ @Test - public void testShouldNotHeadsUp_suppressPeek() throws RemoteException { + public void testShouldHeadsUp_false_suppressPeek() throws RemoteException { ensureStateForHeadsUpWhenAwake(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -339,7 +399,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { * to show as a heads up. */ @Test - public void testShouldNotHeadsUp_lessImportant() throws RemoteException { + public void testShouldHeadsUp_false_lessImportant() throws RemoteException { ensureStateForHeadsUpWhenAwake(); NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); @@ -350,7 +410,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { * If the device is not in use then we shouldn't be shown as heads up. */ @Test - public void testShouldNotHeadsUp_deviceNotInUse() throws RemoteException { + public void testShouldHeadsUp_false_deviceNotInUse() throws RemoteException { ensureStateForHeadsUpWhenAwake(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -364,58 +424,61 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } + /** + * If something wants to suppress this heads up, then it shouldn't be shown as a heads up. + */ @Test - public void testShouldNotHeadsUp_headsUpSuppressed() throws RemoteException { + public void testShouldHeadsUp_false_suppressed() throws RemoteException { ensureStateForHeadsUpWhenAwake(); - - // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up. - mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeHeadsUp); + when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(false); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); + verify(mHeadsUpSuppressor).canHeadsUp(any(), any()); } + /** + * On screen alerts don't happen when the device is in VR Mode. + */ @Test - public void testShouldNotHeadsUpAwake_awakeInterruptsSuppressed() throws RemoteException { - ensureStateForHeadsUpWhenAwake(); - - // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up. - mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeInterruptions); + public void testCanAlertAwakeCommon__false_vrMode() { + ensureStateForAlertAwakeCommon(); + when(mPresenter.isDeviceInVrMode()).thenReturn(true); - NotificationEntry entry = createNotification(IMPORTANCE_HIGH); - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse(); } /** * On screen alerts don't happen when the notification is snoozed. */ @Test - public void testShouldNotHeadsUp_snoozedPackage() { - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + public void testCanAlertAwakeCommon_false_snoozedPackage() { ensureStateForAlertAwakeCommon(); + when(mHeadsUpManager.isSnoozed(any())).thenReturn(true); - when(mHeadsUpManager.isSnoozed(entry.getSbn().getPackageName())).thenReturn(true); - - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse(); } - + /** + * On screen alerts don't happen when that package has just launched fullscreen. + */ @Test - public void testShouldNotHeadsUp_justLaunchedFullscreen() { + public void testCanAlertAwakeCommon_false_justLaunchedFullscreen() { ensureStateForAlertAwakeCommon(); - // On screen alerts don't happen when that package has just launched fullscreen. NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); entry.notifyFullScreenIntentLaunched(); - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); + assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse(); } /** * Bubbles can happen. */ @Test - public void testShouldBubbleUp() { + public void testShouldBubbleUp_true() { ensureStateForBubbleUp(); assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isTrue(); } @@ -424,7 +487,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { * If the notification doesn't have permission to bubble, it shouldn't bubble. */ @Test - public void shouldNotBubbleUp_notAllowedToBubble() { + public void shouldBubbleUp_false_notAllowedToBubble() { ensureStateForBubbleUp(); NotificationEntry entry = createBubble(); @@ -439,7 +502,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { * If the notification isn't a bubble, it should definitely not show as a bubble. */ @Test - public void shouldNotBubbleUp_notABubble() { + public void shouldBubbleUp_false_notABubble() { ensureStateForBubbleUp(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -454,7 +517,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { * If the notification doesn't have bubble metadata, it shouldn't bubble. */ @Test - public void shouldNotBubbleUp_invalidMetadata() { + public void shouldBubbleUp_false_invalidMetadata() { ensureStateForBubbleUp(); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -466,18 +529,24 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse(); } + /** + * If the notification can't heads up in general, it shouldn't bubble. + */ @Test - public void shouldNotBubbleUp_suppressedInterruptions() { + public void shouldBubbleUp_false_alertAwakeCommonFalse() { ensureStateForBubbleUp(); - // If the notification can't heads up in general, it shouldn't bubble. - mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions); + // Make alert common return false by pretending we're in VR mode + when(mPresenter.isDeviceInVrMode()).thenReturn(true); assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse(); } + /** + * If the notification can't heads up in general, it shouldn't bubble. + */ @Test - public void shouldNotBubbleUp_filteredOut() { + public void shouldBubbleUp_false_alertCommonFalse() { ensureStateForBubbleUp(); // Make canAlertCommon false by saying it's filtered out @@ -523,45 +592,20 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { .build(); } - private final NotificationInterruptSuppressor - mSuppressAwakeHeadsUp = - new NotificationInterruptSuppressor() { - @Override - public String getName() { - return "suppressAwakeHeadsUp"; - } - - @Override - public boolean suppressAwakeHeadsUp(NotificationEntry entry) { - return true; - } - }; - - private final NotificationInterruptSuppressor - mSuppressAwakeInterruptions = - new NotificationInterruptSuppressor() { - @Override - public String getName() { - return "suppressAwakeInterruptions"; - } - - @Override - public boolean suppressAwakeInterruptions(NotificationEntry entry) { - return true; - } - }; - - private final NotificationInterruptSuppressor - mSuppressInterruptions = - new NotificationInterruptSuppressor() { - @Override - public String getName() { - return "suppressInterruptions"; - } - - @Override - public boolean suppressInterruptions(NotificationEntry entry) { - return true; + /** + * Testable class overriding constructor. + */ + public static class TestableNotificationInterruptionStateProvider extends + NotificationInterruptionStateProvider { + + TestableNotificationInterruptionStateProvider(Context context, + PowerManager powerManager, IDreamManager dreamManager, + AmbientDisplayConfiguration ambientDisplayConfiguration, + NotificationFilter notificationFilter, + StatusBarStateController statusBarStateController, + BatteryController batteryController) { + super(context, powerManager, dreamManager, ambientDisplayConfiguration, + notificationFilter, batteryController, statusBarStateController); } - }; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java index 62f406ff835a..1b0ed112cea1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java @@ -22,6 +22,8 @@ import android.content.Context; import android.os.UserHandle; import android.service.notification.StatusBarNotification; +import com.android.internal.logging.InstanceId; + /** * Convenience builder for {@link StatusBarNotification} since its constructor is terrifying. * @@ -40,6 +42,7 @@ public class SbnBuilder { private UserHandle mUser = UserHandle.of(0); private String mOverrideGroupKey; private long mPostTime; + private InstanceId mInstanceId; public SbnBuilder() { } @@ -55,6 +58,7 @@ public class SbnBuilder { mUser = source.getUser(); mOverrideGroupKey = source.getOverrideGroupKey(); mPostTime = source.getPostTime(); + mInstanceId = source.getInstanceId(); } public StatusBarNotification build() { @@ -71,7 +75,7 @@ public class SbnBuilder { notification.setBubbleMetadata(mBubbleMetadata); } - return new StatusBarNotification( + StatusBarNotification result = new StatusBarNotification( mPkg, mOpPkg, mId, @@ -82,6 +86,10 @@ public class SbnBuilder { mUser, mOverrideGroupKey, mPostTime); + if (mInstanceId != null) { + result.setInstanceId(mInstanceId); + } + return result; } public SbnBuilder setPkg(String pkg) { @@ -175,4 +183,9 @@ public class SbnBuilder { mBubbleMetadata = data; return this; } + + public SbnBuilder setInstanceId(InstanceId instanceId) { + mInstanceId = instanceId; + return this; + } } 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 92a908033472..261dc829c7e2 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 @@ -26,6 +26,7 @@ import android.os.UserHandle; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; +import com.android.internal.logging.InstanceId; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; @@ -141,6 +142,11 @@ public class NotificationEntryBuilder { return this; } + public NotificationEntryBuilder setInstanceId(InstanceId instanceId) { + mSbnBuilder.setInstanceId(instanceId); + return this; + } + /* Delegated to Notification.Builder (via SbnBuilder) */ public NotificationEntryBuilder setContentTitle(Context context, String contentTitle) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index d826ce1bbdd8..d39b2c202fd9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -16,7 +16,10 @@ package com.android.systemui.statusbar.notification.logging; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING; + import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -34,6 +37,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.internal.logging.InstanceId; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.SysuiTestCase; @@ -43,6 +47,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.logging.nano.Notifications; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.util.concurrency.FakeExecutor; @@ -81,9 +86,10 @@ public class NotificationLoggerTest extends SysuiTestCase { private NotificationEntry mEntry; private TestableNotificationLogger mLogger; - private NotificationEntryListener mNotificationEntryListener; private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); + private NotificationPanelLoggerFake mNotificationPanelLoggerFake = + new NotificationPanelLoggerFake(); @Before public void setUp() { @@ -97,6 +103,7 @@ public class NotificationLoggerTest extends SysuiTestCase { .setUid(TEST_UID) .setNotification(new Notification()) .setUser(UserHandle.CURRENT) + .setInstanceId(InstanceId.fakeInstanceId(1)) .build(); mEntry.setRow(mRow); @@ -105,7 +112,6 @@ public class NotificationLoggerTest extends SysuiTestCase { mExpansionStateLogger); mLogger.setUpWithContainer(mListContainer); verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture()); - mNotificationEntryListener = mEntryListenerCaptor.getValue(); } @Test @@ -164,6 +170,41 @@ public class NotificationLoggerTest extends SysuiTestCase { verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any()); } + @Test + public void testLogPanelShownOnLoggingStart() { + when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry)); + mLogger.startNotificationLogging(); + assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); + assertEquals(false, mNotificationPanelLoggerFake.get(0).isLockscreen); + assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); + Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; + assertEquals(TEST_PACKAGE_NAME, n.packageName); + assertEquals(TEST_UID, n.uid); + assertEquals(1, n.instanceId); + assertEquals(false, n.isGroupSummary); + assertEquals(1 + BUCKET_ALERTING, n.section); + } + + @Test + public void testLogPanelShownHandlesNullInstanceIds() { + // Construct a NotificationEntry like mEntry, but with a null instance id. + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE_NAME) + .setOpPkg(TEST_PACKAGE_NAME) + .setUid(TEST_UID) + .setNotification(new Notification()) + .setUser(UserHandle.CURRENT) + .build(); + entry.setRow(mRow); + + when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry)); + mLogger.startNotificationLogging(); + assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); + assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); + Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; + assertEquals(0, n.instanceId); + } + private class TestableNotificationLogger extends NotificationLogger { TestableNotificationLogger(NotificationListener notificationListener, @@ -173,7 +214,7 @@ public class NotificationLoggerTest extends SysuiTestCase { IStatusBarService barService, ExpansionStateLogger expansionStateLogger) { super(notificationListener, uiBgExecutor, entryManager, statusBarStateController, - expansionStateLogger); + expansionStateLogger, mNotificationPanelLoggerFake); mBarService = barService; // Make this on the current thread so we can wait for it during tests. mHandler = Handler.createAsync(Looper.myLooper()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java new file mode 100644 index 000000000000..7e97629e82e2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java @@ -0,0 +1,51 @@ +/* + * 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.logging; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.logging.nano.Notifications; + +import java.util.ArrayList; +import java.util.List; + +public class NotificationPanelLoggerFake implements NotificationPanelLogger { + private List<CallRecord> mCalls = new ArrayList<>(); + + List<CallRecord> getCalls() { + return mCalls; + } + + CallRecord get(int index) { + return mCalls.get(index); + } + + @Override + public void logPanelShown(boolean isLockscreen, + List<NotificationEntry> visibleNotifications) { + mCalls.add(new CallRecord(isLockscreen, + NotificationPanelLogger.toNotificationProto(visibleNotifications))); + } + + public static class CallRecord { + public boolean isLockscreen; + public Notifications.NotificationList list; + CallRecord(boolean isLockscreen, Notifications.NotificationList list) { + this.isLockscreen = isLockscreen; + this.list = list; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index a21a047d9a70..5d0349dbbb60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -56,12 +56,12 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -108,7 +108,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock private NotificationEntryListener mEntryListener; @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback; @Mock private HeadsUpManager mHeadsUpManager; - @Mock private NotificationInterruptStateProvider mNotificationInterruptionStateProvider; + @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationGutsManager mGutsManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index b9c5b7c02b1c..1e4df272b02b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -67,10 +67,10 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -183,7 +183,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class), mock(NotificationLockscreenUserManager.class), mKeyguardStateController, - mock(NotificationInterruptStateProvider.class), mock(MetricsLogger.class), + mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class), mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor, mActivityIntentHelper, mBubbleController, mShadeController, mFeatureFlags, mNotifPipeline, mNotifCollection) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 318e9b87fa70..b9d2d229cd69 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -16,9 +16,8 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; @@ -36,7 +35,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; -import com.android.systemui.ForegroundServiceNotificationListener; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -50,12 +48,12 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -64,7 +62,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import java.util.ArrayList; @@ -75,9 +72,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private StatusBarNotificationPresenter mStatusBarNotificationPresenter; - private NotificationInterruptStateProvider mNotificationInterruptStateProvider = - mock(NotificationInterruptStateProvider.class); - private NotificationInterruptSuppressor mInterruptSuppressor; private CommandQueue mCommandQueue; private FakeMetricsLogger mMetricsLogger; private ShadeController mShadeController = mock(ShadeController.class); @@ -101,11 +95,11 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mDependency.injectMockDependency(NotificationViewHierarchyManager.class); mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class); mDependency.injectMockDependency(NotificationLockscreenUserManager.class); + mDependency.injectMockDependency(NotificationInterruptionStateProvider.class); mDependency.injectMockDependency(NotificationMediaManager.class); mDependency.injectMockDependency(VisualStabilityManager.class); mDependency.injectMockDependency(NotificationGutsManager.class); mDependency.injectMockDependency(NotificationShadeWindowController.class); - mDependency.injectMockDependency(ForegroundServiceNotificationListener.class); NotificationEntryManager entryManager = mDependency.injectMockDependency(NotificationEntryManager.class); when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>()); @@ -113,25 +107,18 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { NotificationShadeWindowView notificationShadeWindowView = mock(NotificationShadeWindowView.class); when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources()); - mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext, mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class), notificationShadeWindowView, mock(NotificationListContainerViewGroup.class), mock(DozeScrimController.class), mock(ScrimController.class), mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class), - mock(KeyguardStateController.class), + mock(NotificationAlertingManager.class), mock(KeyguardStateController.class), mock(KeyguardIndicationController.class), mStatusBar, - mock(ShadeControllerImpl.class), mCommandQueue, mInitController, - mNotificationInterruptStateProvider); - mInitController.executePostInitTasks(); - ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor = - ArgumentCaptor.forClass(NotificationInterruptSuppressor.class); - verify(mNotificationInterruptStateProvider).addSuppressor(suppressorCaptor.capture()); - mInterruptSuppressor = suppressorCaptor.getValue(); + mock(ShadeControllerImpl.class), mCommandQueue, mInitController); } @Test - public void testSuppressHeadsUp_disabledStatusBar() { + public void testHeadsUp_disabledStatusBar() { Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") @@ -143,12 +130,12 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { false /* animate */); TestableLooper.get(this).processAllMessages(); - assertTrue("The panel should suppress heads up while disabled", - mInterruptSuppressor.suppressAwakeHeadsUp(entry)); + assertFalse("The panel shouldn't allow heads up while disabled", + mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn())); } @Test - public void testSuppressHeadsUp_disabledNotificationShade() { + public void testHeadsUp_disabledNotificationShade() { Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() .setPkg("a") @@ -160,39 +147,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { false /* animate */); TestableLooper.get(this).processAllMessages(); - assertTrue("The panel should suppress interruptions while notification shade " - + "disabled", - mInterruptSuppressor.suppressAwakeHeadsUp(entry)); - } - - @Test - public void testSuppressInterruptions_vrMode() { - Notification n = new Notification.Builder(getContext(), "a").build(); - NotificationEntry entry = new NotificationEntryBuilder() - .setPkg("a") - .setOpPkg("a") - .setTag("a") - .setNotification(n) - .build(); - mStatusBarNotificationPresenter.mVrMode = true; - - assertTrue("Vr mode should suppress interruptions", - mInterruptSuppressor.suppressAwakeInterruptions(entry)); - } - - @Test - public void testSuppressInterruptions_statusBarAlertsDisabled() { - Notification n = new Notification.Builder(getContext(), "a").build(); - NotificationEntry entry = new NotificationEntryBuilder() - .setPkg("a") - .setOpPkg("a") - .setTag("a") - .setNotification(n) - .build(); - when(mStatusBar.areNotificationAlertsDisabled()).thenReturn(true); - - assertTrue("StatusBar alerts disabled shouldn't allow interruptions", - mInterruptSuppressor.suppressInterruptions(entry)); + assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled", + mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn())); } @Test @@ -216,3 +172,4 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } } } + diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index d9f4d4b8f758..e4079275ddfc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -42,7 +42,7 @@ import android.app.Notification; import android.app.StatusBarManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; -import android.content.ContentResolver; +import android.content.Context; import android.content.IntentFilter; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.fingerprint.FingerprintManager; @@ -109,18 +109,20 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.init.NotificationsController; -import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; @@ -128,7 +130,6 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; @@ -160,7 +161,7 @@ public class StatusBarTest extends SysuiTestCase { private StatusBar mStatusBar; private FakeMetricsLogger mMetricsLogger; private PowerManager mPowerManager; - private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider; + private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider; @Mock private NotificationsController mNotificationsController; @Mock private LightBarController mLightBarController; @@ -178,6 +179,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private DozeScrimController mDozeScrimController; @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; @Mock private BiometricUnlockController mBiometricUnlockController; + @Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor; @Mock private VisualStabilityManager mVisualStabilityManager; @Mock private NotificationListener mNotificationListener; @Mock private KeyguardViewMediator mKeyguardViewMediator; @@ -190,9 +192,10 @@ public class StatusBarTest extends SysuiTestCase { @Mock private StatusBarNotificationPresenter mNotificationPresenter; @Mock private NotificationEntryListener mEntryListener; @Mock private NotificationFilter mNotificationFilter; - @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration; + @Mock private NotificationAlertingManager mNotificationAlertingManager; @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration; @Mock private NotificationShadeWindowView mNotificationShadeWindowView; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private AssistManager mAssistManager; @@ -260,12 +263,10 @@ public class StatusBarTest extends SysuiTestCase { mPowerManager = new PowerManager(mContext, powerManagerService, thermalService, Handler.createAsync(Looper.myLooper())); - mNotificationInterruptStateProvider = - new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), - mPowerManager, + mNotificationInterruptionStateProvider = + new TestableNotificationInterruptionStateProvider(mContext, mPowerManager, mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter, - mStatusBarStateController, mBatteryController, mHeadsUpManager, - new Handler(TestableLooper.get(this).getLooper())); + mStatusBarStateController, mBatteryController); mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class)); mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class)); @@ -273,7 +274,7 @@ public class StatusBarTest extends SysuiTestCase { mMetricsLogger = new FakeMetricsLogger(); NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener, mUiBgExecutor, mock(NotificationEntryManager.class), mStatusBarStateController, - mExpansionStateLogger); + mExpansionStateLogger, new NotificationPanelLoggerFake()); notificationLogger.setVisibilityReporter(mock(Runnable.class)); when(mCommandQueue.asBinder()).thenReturn(new Binder()); @@ -298,6 +299,9 @@ public class StatusBarTest extends SysuiTestCase { return null; }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any()); + mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter, + mHeadsUpManager, mHeadsUpSuppressor); + when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle(); @@ -344,9 +348,10 @@ public class StatusBarTest extends SysuiTestCase { ), mNotificationGutsManager, notificationLogger, - mNotificationInterruptStateProvider, + mNotificationInterruptionStateProvider, mNotificationViewHierarchyManager, mKeyguardViewMediator, + mNotificationAlertingManager, new DisplayMetrics(), mMetricsLogger, mUiBgExecutor, @@ -557,6 +562,7 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); + when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a") .setGroup("a") @@ -572,7 +578,7 @@ public class StatusBarTest extends SysuiTestCase { .setImportance(IMPORTANCE_HIGH) .build(); - assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry)); + assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); } @Test @@ -581,6 +587,7 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); + when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a") .setGroup("a") @@ -596,7 +603,7 @@ public class StatusBarTest extends SysuiTestCase { .setImportance(IMPORTANCE_HIGH) .build(); - assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry)); + assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); } @Test @@ -605,6 +612,7 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); + when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a").build(); @@ -617,7 +625,7 @@ public class StatusBarTest extends SysuiTestCase { .setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK) .build(); - assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry)); + assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); } @Test @@ -626,6 +634,7 @@ public class StatusBarTest extends SysuiTestCase { when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false); when(mDreamManager.isDreaming()).thenReturn(false); + when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true); Notification n = new Notification.Builder(getContext(), "a").build(); @@ -637,7 +646,7 @@ public class StatusBarTest extends SysuiTestCase { .setImportance(IMPORTANCE_HIGH) .build(); - assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry)); + assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); } @Test @@ -863,21 +872,19 @@ public class StatusBarTest extends SysuiTestCase { verify(mDozeServiceHost).setDozeSuppressed(false); } - public static class TestableNotificationInterruptStateProviderImpl extends - NotificationInterruptStateProviderImpl { + public static class TestableNotificationInterruptionStateProvider extends + NotificationInterruptionStateProvider { - TestableNotificationInterruptStateProviderImpl( - ContentResolver contentResolver, + TestableNotificationInterruptionStateProvider( + Context context, PowerManager powerManager, IDreamManager dreamManager, AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter filter, StatusBarStateController controller, - BatteryController batteryController, - HeadsUpManager headsUpManager, - Handler mainHandler) { - super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter, - batteryController, controller, headsUpManager, mainHandler); + BatteryController batteryController) { + super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter, + batteryController, controller); mUseHeadsUp = true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index a0d551c743c4..831925f8bb26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -235,7 +235,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { subs.add(subscription); } when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs); - when(mMockSm.getActiveAndHiddenSubscriptionInfoList()).thenReturn(subs); + when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(subs); mNetworkController.doUpdateMobileControllers(); } diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 5b73dd53a285..2fbba68f1e03 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -62,26 +62,14 @@ java_library { apex_available: ["com.android.tethering"], } -droidstubs { - name: "framework-tethering-stubs-sources", - defaults: ["framework-module-stubs-defaults-module_libs_api"], +stubs_defaults { + name: "framework-tethering-stubs-defaults", srcs: [ "src/android/net/TetheredClient.java", "src/android/net/TetheringManager.java", "src/android/net/TetheringConstants.java", ], - libs: [ - "tethering-aidl-interfaces-java", - "framework-all", - ], - sdk_version: "core_platform", -} - -java_library { - name: "framework-tethering-stubs", - srcs: [":framework-tethering-stubs-sources"], - libs: ["framework-all"], - sdk_version: "core_platform", + libs: ["tethering-aidl-interfaces-java"], } filegroup { @@ -101,3 +89,53 @@ filegroup { ], path: "src" } + +droidstubs { + name: "framework-tethering-stubs-srcs-publicapi", + defaults: [ + "framework-module-stubs-defaults-publicapi", + "framework-tethering-stubs-defaults", + ], +} + +droidstubs { + name: "framework-tethering-stubs-srcs-systemapi", + defaults: [ + "framework-module-stubs-defaults-systemapi", + "framework-tethering-stubs-defaults", + ], +} + +droidstubs { + name: "framework-tethering-api-module_libs_api", + defaults: [ + "framework-module-api-defaults-module_libs_api", + "framework-tethering-stubs-defaults", + ], +} + +droidstubs { + name: "framework-tethering-stubs-srcs-module_libs_api", + defaults: [ + "framework-module-stubs-defaults-module_libs_api", + "framework-tethering-stubs-defaults", + ], +} + +java_library { + name: "framework-tethering-stubs-publicapi", + srcs: [":framework-tethering-stubs-srcs-publicapi"], + sdk_version: "current", +} + +java_library { + name: "framework-tethering-stubs-systemapi", + srcs: [":framework-tethering-stubs-srcs-systemapi"], + sdk_version: "system_current", +} + +java_library { + name: "framework-tethering-stubs-module_libs_api", + srcs: [":framework-tethering-stubs-srcs-module_libs_api"], + sdk_version: "module_current", +} diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java index cc36f4a9c516..a402ffa47355 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java @@ -288,10 +288,18 @@ public class OffloadController { @Override public void setLimit(String iface, long quotaBytes) { - mLog.i("setLimit: " + iface + "," + quotaBytes); // Listen for all iface is necessary since upstream might be changed after limit // is set. mHandler.post(() -> { + final Long curIfaceQuota = mInterfaceQuotas.get(iface); + + // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE, + // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not + // useful to set it multiple times. + // Otherwise, the quota needs to be updated to tell HAL to re-count from now even + // if the quota is the same as the existing one. + if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return; + if (quotaBytes == QUOTA_UNLIMITED) { mInterfaceQuotas.remove(iface); } else { @@ -323,7 +331,6 @@ public class OffloadController { @Override public void requestStatsUpdate(int token) { - mLog.i("requestStatsUpdate: " + token); // Do not attempt to update stats by querying the offload HAL // synchronously from a different thread than the Handler thread. http://b/64771555. mHandler.post(() -> { diff --git a/services/Android.bp b/services/Android.bp index ef47867eed48..c4be0032ade8 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -78,7 +78,7 @@ java_library { libs: [ "android.hidl.manager-V1.0-java", - "framework-tethering-stubs", + "framework-tethering-stubs-module_libs_api", ], plugins: [ diff --git a/services/api/current.txt b/services/api/current.txt index 8c90165eeb4a..9bbb3efcc7c2 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -3,9 +3,9 @@ package com.android.permission.persistence { public interface RuntimePermissionsPersistence { method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance(); - method public void deleteAsUser(@NonNull android.os.UserHandle); - method @Nullable public com.android.permission.persistence.RuntimePermissionsState readAsUser(@NonNull android.os.UserHandle); - method public void writeAsUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle); + method public void deleteForUser(@NonNull android.os.UserHandle); + method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle); + method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle); } public final class RuntimePermissionsState { @@ -17,7 +17,7 @@ package com.android.permission.persistence { field public static final int NO_VERSION = -1; // 0xffffffff } - public static class RuntimePermissionsState.PermissionState { + public static final class RuntimePermissionsState.PermissionState { ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int); method public int getFlags(); method @NonNull public String getName(); @@ -30,9 +30,9 @@ package com.android.role.persistence { public interface RolesPersistence { method @NonNull public static com.android.role.persistence.RolesPersistence createInstance(); - method public void deleteAsUser(@NonNull android.os.UserHandle); - method @Nullable public com.android.role.persistence.RolesState readAsUser(@NonNull android.os.UserHandle); - method public void writeAsUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle); + method public void deleteForUser(@NonNull android.os.UserHandle); + method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle); + method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle); } public final class RolesState { diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 2420e69b5d90..e73f9ce4f7c0 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -37,7 +37,7 @@ import android.os.ICancellationSignal; import android.os.RemoteException; import android.os.SystemClock; import android.service.autofill.Dataset; -import android.service.autofill.InlinePresentation; +import android.service.autofill.InlineAction; import android.service.autofill.augmented.AugmentedAutofillService; import android.service.autofill.augmented.IAugmentedAutofillService; import android.service.autofill.augmented.IFillCallback; @@ -167,7 +167,7 @@ final class RemoteAugmentedAutofillService new IFillCallback.Stub() { @Override public void onSuccess(@Nullable List<Dataset> inlineSuggestionsData, - @Nullable List<InlinePresentation> inlineActions) { + @Nullable List<InlineAction> inlineActions) { mCallbacks.resetLastResponse(); maybeRequestShowInlineSuggestions(sessionId, inlineSuggestionsRequest, inlineSuggestionsData, @@ -237,7 +237,7 @@ final class RemoteAugmentedAutofillService private void maybeRequestShowInlineSuggestions(int sessionId, @Nullable InlineSuggestionsRequest request, @Nullable List<Dataset> inlineSuggestionsData, - @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId focusedId, + @Nullable List<InlineAction> inlineActions, @NonNull AutofillId focusedId, @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback, @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { @@ -250,7 +250,7 @@ final class RemoteAugmentedAutofillService final InlineSuggestionsResponse inlineSuggestionsResponse = InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse( - request, inlineSuggestionsData, inlineActions, focusedId, mContext, + request, inlineSuggestionsData, inlineActions, focusedId, dataset -> { mCallbacks.logAugmentedAutofillSelected(sessionId, dataset.getId()); diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index f14a7e906c5d..826500952e60 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -18,6 +18,7 @@ package com.android.server.autofill; import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; @@ -137,7 +138,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private final Handler mHandler; private final Object mLock; private final AutoFillUI mUi; - private final Context mContext; private final MetricsLogger mMetricsLogger = new MetricsLogger(); @@ -625,7 +625,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + ", flags=" + flags + ")"); } mForAugmentedAutofillOnly = true; - triggerAugmentedAutofillLocked(); + triggerAugmentedAutofillLocked(flags); return; } @@ -695,7 +695,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mLock = lock; mUi = ui; mHandler = handler; - mContext = context; mRemoteFillService = serviceComponentName == null ? null : new RemoteFillService(context, serviceComponentName, userId, this, bindInstantServiceAllowed); @@ -836,7 +835,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Although "standard" autofill is disabled, it might still trigger augmented autofill - if (triggerAugmentedAutofillLocked() != null) { + if (triggerAugmentedAutofillLocked(requestFlags) != null) { mForAugmentedAutofillOnly = true; if (sDebug) { Slog.d(TAG, "Service disabled autofill for " + mComponentName @@ -2467,7 +2466,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // triggered augmented autofill if (!isSameViewEntered) { if (sDebug) Slog.d(TAG, "trigger augmented autofill."); - triggerAugmentedAutofillLocked(); + triggerAugmentedAutofillLocked(flags); } else { if (sDebug) Slog.d(TAG, "skip augmented autofill for same view."); } @@ -2680,10 +2679,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState InlineSuggestionsResponse inlineSuggestionsResponse = InlineSuggestionFactory.createInlineSuggestionsResponse( inlineSuggestionsRequest.get(), - response, filterText, response.getInlineActions(), mCurrentViewId, mContext, + response, filterText, response.getInlineActions(), mCurrentViewId, this, () -> { synchronized (mLock) { - requestHideFillUi(mCurrentViewId); + mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId); } }, remoteRenderService); if (inlineSuggestionsResponse == null) { @@ -2865,8 +2864,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // The default autofill service cannot fullfill the request, let's check if the augmented // autofill service can. - mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(); - if (mAugmentedAutofillDestroyer == null) { + mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags); + if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) { if (sVerbose) { Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot " + "be augmented. AutofillableIds: " + autofillableIds); @@ -2876,8 +2875,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState removeSelf(); } else { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null but " - + "it can be augmented. AutofillableIds: " + autofillableIds); + if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { + Slog.v(TAG, "keeping session " + id + " when service returned null and " + + "augmented service is disabled for password fields. " + + "AutofillableIds: " + autofillableIds); + } else { + Slog.v(TAG, "keeping session " + id + " when service returned null but " + + "it can be augmented. AutofillableIds: " + autofillableIds); + } } mAugmentedAutofillableIds = autofillableIds; try { @@ -2896,7 +2901,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO(b/123099468): might need to call it in other places, like when the service returns a // non-null response but without datasets (for example, just SaveInfo) @GuardedBy("mLock") - private Runnable triggerAugmentedAutofillLocked() { + private Runnable triggerAugmentedAutofillLocked(int flags) { + // (TODO: b/141703197) Fix later by passing info to service. + if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { + return null; + } + // Check if Smart Suggestions is supported... final @SmartSuggestionMode int supportedModes = mService .getSupportedSmartSuggestionModesLocked(); 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 4cf4463feaad..ee59d89b7f15 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -21,12 +21,13 @@ import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.Context; +import android.content.IntentSender; import android.os.IBinder; import android.os.RemoteException; import android.service.autofill.Dataset; import android.service.autofill.FillResponse; import android.service.autofill.IInlineSuggestionUiCallback; +import android.service.autofill.InlineAction; import android.service.autofill.InlinePresentation; import android.text.TextUtils; import android.util.Slog; @@ -39,7 +40,6 @@ import android.view.inputmethod.InlineSuggestion; import android.view.inputmethod.InlineSuggestionInfo; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; -import android.widget.Toast; import com.android.internal.view.inline.IInlineContentCallback; import com.android.internal.view.inline.IInlineContentProvider; @@ -73,8 +73,8 @@ public final class InlineSuggestionFactory { @Nullable public static InlineSuggestionsResponse createInlineSuggestionsResponse( @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response, - @Nullable String filterText, @Nullable List<InlinePresentation> inlineActions, - @NonNull AutofillId autofillId, @NonNull Context context, + @Nullable String filterText, @Nullable List<InlineAction> inlineActions, + @NonNull AutofillId autofillId, @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called"); @@ -97,7 +97,7 @@ public final class InlineSuggestionFactory { response.getAuthentication() == null ? null : response.getInlinePresentation(); return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, response.getDatasets(), filterText, inlineAuthentication, inlineActions, autofillId, - context, onErrorCallback, onClickFactory, remoteRenderService); + onErrorCallback, onClickFactory, remoteRenderService); } /** @@ -107,15 +107,14 @@ public final class InlineSuggestionFactory { @Nullable public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse( @NonNull InlineSuggestionsRequest request, @NonNull List<Dataset> datasets, - @Nullable List<InlinePresentation> inlineActions, - @NonNull AutofillId autofillId, @NonNull Context context, + @Nullable List<InlineAction> inlineActions, @NonNull AutofillId autofillId, @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called"); return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request, datasets, /* filterText= */ null, /* inlineAuthentication= */ null, - inlineActions, autofillId, context, onErrorCallback, + inlineActions, autofillId, onErrorCallback, (dataset, datasetIndex) -> inlineSuggestionUiCallback.autofill(dataset), remoteRenderService); } @@ -125,9 +124,8 @@ public final class InlineSuggestionFactory { boolean isAugmented, @NonNull InlineSuggestionsRequest request, @Nullable List<Dataset> datasets, @Nullable String filterText, @Nullable InlinePresentation inlineAuthentication, - @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId, - @NonNull Context context, @NonNull Runnable onErrorCallback, - @NonNull BiConsumer<Dataset, Integer> onClickFactory, + @Nullable List<InlineAction> inlineActions, @NonNull AutofillId autofillId, + @NonNull Runnable onErrorCallback, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>(); @@ -169,14 +167,14 @@ public final class InlineSuggestionFactory { inlineSuggestions.add(inlineSuggestion); } - // We should only add inline actions if there is at least one suggestion. - if (!inlineSuggestions.isEmpty() && inlineActions != null) { - for (InlinePresentation inlinePresentation : inlineActions) { - final InlineSuggestion inlineAction = createInlineAction(isAugmented, context, - mergedInlinePresentation(request, 0, inlinePresentation), + if (inlineActions != null) { + for (InlineAction inlineAction : inlineActions) { + final InlineSuggestion inlineActionSuggestion = createInlineAction(isAugmented, + mergedInlinePresentation(request, 0, inlineAction.getInlinePresentation()), + inlineAction.getAction(), remoteRenderService, onErrorCallback, request.getHostInputToken(), request.getHostDisplayId()); - inlineSuggestions.add(inlineAction); + inlineSuggestions.add(inlineActionSuggestion); } } return new InlineSuggestionsResponse(inlineSuggestions); @@ -215,22 +213,30 @@ public final class InlineSuggestionFactory { private static InlineSuggestion createInlineAction(boolean isAugmented, - @NonNull Context context, - @NonNull InlinePresentation inlinePresentation, + @NonNull InlinePresentation presentation, + @NonNull IntentSender action, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken, int displayId) { final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( - inlinePresentation.getInlinePresentationSpec(), + presentation.getInlinePresentationSpec(), isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM : InlineSuggestionInfo.SOURCE_AUTOFILL, - inlinePresentation.getAutofillHints(), - InlineSuggestionInfo.TYPE_ACTION, inlinePresentation.isPinned()); + presentation.getAutofillHints(), InlineSuggestionInfo.TYPE_ACTION, + presentation.isPinned()); final Runnable onClickAction = () -> { - Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show(); + try { + // TODO(b/150499490): route the intent to the client app to have it fired there, + // so that it will appear as a part of the same task as the client app (similar + // to the authentication flow). + action.sendIntent(null, 0, null, null, null); + } catch (IntentSender.SendIntentException e) { + onErrorCallback.run(); + Slog.w(TAG, "Error sending inline action intent"); + } }; return new InlineSuggestion(inlineSuggestionInfo, - createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback, + createInlineContentProvider(presentation, onClickAction, onErrorCallback, remoteRenderService, hostInputToken, displayId)); } diff --git a/services/core/Android.bp b/services/core/Android.bp index 4cc65900d099..942d5633e381 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -99,7 +99,7 @@ java_library_static { "android.hardware.tv.cec-V1.0-java", "android.hardware.vibrator-java", "app-compat-annotations", - "framework-tethering-stubs", + "framework-tethering-stubs-module_libs_api", "ike-stubs", ], diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index a4a42bcaecb1..03ca1c610f82 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -85,6 +85,7 @@ import java.util.LinkedList; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -272,6 +273,46 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + public boolean onFactoryReset() { + // Wait for stable state if bluetooth is temporary state. + int state = getState(); + if (state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_OFF) { + if (!waitForState(Set.of(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_ON))) { + return false; + } + } + + // Clear registered LE apps to force shut-off Bluetooth + clearBleApps(); + state = getState(); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth == null) { + return false; + } + if (state == BluetoothAdapter.STATE_BLE_ON) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, + mContext.getPackageName(), false); + mBluetooth.onBrEdrDown(); + return true; + } else if (state == BluetoothAdapter.STATE_ON) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, + mContext.getPackageName(), false); + mBluetooth.disable(); + return true; + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to shutdown Bluetooth", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + return false; + } + public void onAirplaneModeChanged() { synchronized (this) { if (isBluetoothPersistedStateOn()) { @@ -1670,7 +1711,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by "waitForOnOff(false, true)". + // is accomplished by + // "waitForState(Set.of(BluetoothAdapter.STATE_OFF))". // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1680,7 +1722,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. The delay time is backed off if Bluetooth // continuously failed to turn on itself. // - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); @@ -1693,10 +1735,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); if (mEnable && mBluetooth != null) { - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); mEnable = false; handleDisable(); - waitForOnOff(false, false); + waitForState(Set.of(BluetoothAdapter.STATE_OFF, + BluetoothAdapter.STATE_TURNING_ON, + BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_BLE_TURNING_ON, + BluetoothAdapter.STATE_BLE_ON, + BluetoothAdapter.STATE_BLE_TURNING_OFF)); } else { mEnable = false; handleDisable(); @@ -1819,9 +1866,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (!mEnable) { - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); handleDisable(); - waitForOnOff(false, false); + 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)); } break; } @@ -1853,7 +1905,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { == BluetoothAdapter.STATE_OFF)) { if (mEnable) { Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); @@ -1982,7 +2034,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mState = BluetoothAdapter.STATE_TURNING_ON; } - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); if (mState == BluetoothAdapter.STATE_TURNING_ON) { bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); @@ -1997,7 +2049,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF); - boolean didDisableTimeout = !waitForOnOff(false, true); + boolean didDisableTimeout = + !waitForState(Set.of(BluetoothAdapter.STATE_OFF)); bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF); @@ -2243,12 +2296,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - /** - * if on is true, wait for state become ON - * if off is true, wait for state become OFF - * if both on and off are false, wait for state not ON - */ - private boolean waitForOnOff(boolean on, boolean off) { + private boolean waitForState(Set<Integer> states) { int i = 0; while (i < 10) { try { @@ -2256,18 +2304,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth == null) { break; } - if (on) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) { - return true; - } - } else if (off) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) { - return true; - } - } else { - if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) { - return true; - } + if (states.contains(mBluetooth.getState())) { + return true; } } catch (RemoteException e) { Slog.e(TAG, "getState()", e); @@ -2275,14 +2313,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } finally { mBluetoothLock.readLock().unlock(); } - if (on || off) { - SystemClock.sleep(300); - } else { - SystemClock.sleep(50); - } + SystemClock.sleep(300); i++; } - Slog.e(TAG, "waitForOnOff time out"); + Slog.e(TAG, "waitForState " + states + " time out"); return false; } @@ -2343,7 +2377,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.getPackageName(), false); handleDisable(); - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); sendBluetoothServiceDownCallback(); @@ -2533,6 +2567,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return "USER_SWITCH"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING: return "RESTORE_USER_SETTING"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET: + return "FACTORY_RESET"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: default: return "UNKNOWN[" + reason + "]"; } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ff41d1cc67af..7287a44600fa 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -273,9 +273,6 @@ public class ConnectivityService extends IConnectivityManager.Stub // connect anyway?" dialog after the user selects a network that doesn't validate. private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000; - // How long to dismiss network notification. - private static final int TIMEOUT_NOTIFICATION_DELAY_MS = 20 * 1000; - // Default to 30s linger time-out. Modifiable only for testing. private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger"; private static final int DEFAULT_LINGER_DELAY_MS = 30_000; @@ -523,18 +520,13 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_PROVISIONING_NOTIFICATION = 43; /** - * This event can handle dismissing notification by given network id. - */ - private static final int EVENT_TIMEOUT_NOTIFICATION = 44; - - /** * Used to specify whether a network should be used even if connectivity is partial. * arg1 = whether to accept the network if its connectivity is partial (1 for true or 0 for * false) * arg2 = whether to remember this choice in the future (1 for true or 0 for false) * obj = network */ - private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45; + private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 44; /** * Event for NetworkMonitor to inform ConnectivityService that the probe status has changed. @@ -543,7 +535,7 @@ public class ConnectivityService extends IConnectivityManager.Stub * arg1 = A bitmask to describe which probes are completed. * arg2 = A bitmask to describe which probes are successful. */ - public static final int EVENT_PROBE_STATUS_CHANGED = 46; + public static final int EVENT_PROBE_STATUS_CHANGED = 45; /** * Event for NetworkMonitor to inform ConnectivityService that captive portal data has changed. @@ -551,7 +543,7 @@ public class ConnectivityService extends IConnectivityManager.Stub * arg2 = netId * obj = captive portal data */ - private static final int EVENT_CAPPORT_DATA_CHANGED = 47; + private static final int EVENT_CAPPORT_DATA_CHANGED = 46; /** * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification @@ -2877,13 +2869,6 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0); final boolean wasValidated = nai.lastValidated; final boolean wasDefault = isDefaultNetwork(nai); - // Only show a connected notification if the network is pending validation - // after the captive portal app was open, and it has now validated. - if (nai.captivePortalValidationPending && valid) { - // User is now logged in, network validated. - nai.captivePortalValidationPending = false; - showNetworkNotification(nai, NotificationType.LOGGED_IN); - } if (DBG) { final String logMsg = !TextUtils.isEmpty(redirectUrl) @@ -3764,12 +3749,6 @@ public class ConnectivityService extends IConnectivityManager.Stub new CaptivePortal(new CaptivePortalImpl(network).asBinder())); appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); - // This runs on a random binder thread, but getNetworkAgentInfoForNetwork is thread-safe, - // and captivePortalValidationPending is volatile. - final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); - if (nai != null) { - nai.captivePortalValidationPending = true; - } Binder.withCleanCallingIdentity(() -> mContext.startActivityAsUser(appIntent, UserHandle.CURRENT)); } @@ -3888,14 +3867,6 @@ public class ConnectivityService extends IConnectivityManager.Stub final String action; final boolean highPriority; switch (type) { - case LOGGED_IN: - action = Settings.ACTION_WIFI_SETTINGS; - mHandler.removeMessages(EVENT_TIMEOUT_NOTIFICATION); - mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NOTIFICATION, - nai.network.netId, 0), TIMEOUT_NOTIFICATION_DELAY_MS); - // High priority because it is a direct result of the user logging in to a portal. - highPriority = true; - break; case NO_INTERNET: action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED; // High priority because it is only displayed for explicitly selected networks. @@ -3923,7 +3894,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } Intent intent = new Intent(action); - if (type != NotificationType.LOGGED_IN && type != NotificationType.PRIVATE_DNS_BROKEN) { + if (type != NotificationType.PRIVATE_DNS_BROKEN) { intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClassName("com.android.settings", @@ -4139,9 +4110,6 @@ public class ConnectivityService extends IConnectivityManager.Stub case EVENT_DATA_SAVER_CHANGED: handleRestrictBackgroundChanged(toBool(msg.arg1)); break; - case EVENT_TIMEOUT_NOTIFICATION: - mNotifier.clearNotification(msg.arg1, NotificationType.LOGGED_IN); - break; } } } diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index b464422e9e3d..2cfe404f8c6f 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -161,6 +161,9 @@ public class PackageWatchdog { private final Runnable mSaveToFile = this::saveToFile; private final SystemClock mSystemClock; private final BootThreshold mBootThreshold; + // The set of packages that have been synced with the ExplicitHealthCheckController + @GuardedBy("mLock") + private Set<String> mRequestedHealthCheckPackages = new ArraySet<>(); @GuardedBy("mLock") private boolean mIsPackagesReady; // Flag to control whether explicit health checks are supported or not @@ -624,17 +627,22 @@ public class PackageWatchdog { * @see #syncRequestsAsync */ private void syncRequests() { - Set<String> packages = null; + boolean syncRequired = false; synchronized (mLock) { if (mIsPackagesReady) { - packages = getPackagesPendingHealthChecksLocked(); + Set<String> packages = getPackagesPendingHealthChecksLocked(); + if (!packages.equals(mRequestedHealthCheckPackages)) { + syncRequired = true; + mRequestedHealthCheckPackages = packages; + } } // else, we will sync requests when packages become ready } // Call outside lock to avoid holding lock when calling into the controller. - if (packages != null) { - Slog.i(TAG, "Syncing health check requests for packages: " + packages); - mHealthCheckController.syncRequests(packages); + if (syncRequired) { + Slog.i(TAG, "Syncing health check requests for packages: " + + mRequestedHealthCheckPackages); + mHealthCheckController.syncRequests(mRequestedHealthCheckPackages); } } diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index b43ae36c7ef5..cfb79aa3a210 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -100,7 +100,7 @@ public class ServiceWatcher implements ServiceConnection { @Nullable public final ComponentName component; @UserIdInt public final int userId; - private ServiceInfo(ResolveInfo resolveInfo, int currentUserId) { + ServiceInfo(ResolveInfo resolveInfo, int currentUserId) { Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null); Bundle metadata = resolveInfo.serviceInfo.metaData; @@ -316,6 +316,7 @@ public class ServiceWatcher implements ServiceConnection { } mContext.unbindService(this); + onServiceDisconnected(mServiceInfo.component); mServiceInfo = ServiceInfo.NONE; } @@ -339,15 +340,13 @@ public class ServiceWatcher implements ServiceConnection { @Override public final void onServiceConnected(ComponentName component, IBinder binder) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + Preconditions.checkState(mBinder == null); if (D) { Log.i(TAG, getLogPrefix() + " connected to " + component.toShortString()); } mBinder = binder; - - // we always run the on bind callback even if we know that the binder is dead already so - // that there are always balance pairs of bind/unbind callbacks if (mOnBind != null) { try { mOnBind.run(binder); @@ -357,19 +356,16 @@ public class ServiceWatcher implements ServiceConnection { Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e); } } - - try { - // setting the binder to null lets us skip queued transactions - binder.linkToDeath(() -> mBinder = null, 0); - } catch (RemoteException e) { - mBinder = null; - } } @Override public final void onServiceDisconnected(ComponentName component) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + if (mBinder == null) { + return; + } + if (D) { Log.i(TAG, getLogPrefix() + " disconnected from " + component.toShortString()); } @@ -391,18 +387,18 @@ public class ServiceWatcher implements ServiceConnection { onBestServiceChanged(true); } - private void onUserSwitched(@UserIdInt int userId) { + void onUserSwitched(@UserIdInt int userId) { mCurrentUserId = userId; onBestServiceChanged(false); } - private void onUserUnlocked(@UserIdInt int userId) { + void onUserUnlocked(@UserIdInt int userId) { if (userId == mCurrentUserId) { onBestServiceChanged(false); } } - private void onPackageChanged(String packageName) { + void onPackageChanged(String packageName) { // force a rebind if the changed package was the currently connected package String currentPackageName = mServiceInfo.component != null ? mServiceInfo.component.getPackageName() : null; diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 14154339ac4a..7dedad7f2fea 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -167,14 +167,13 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { @Override public String toString() { - return "{callingPackage=" + callingPackage + " binder=" + binder - + " callback=" + callback + return "{callingPackage=" + pii(callingPackage) + " callerUid=" + callerUid + " binder=" + + binder + " callback=" + callback + " onSubscriptionsChangedListenererCallback=" + onSubscriptionsChangedListenerCallback + " onOpportunisticSubscriptionsChangedListenererCallback=" - + onOpportunisticSubscriptionsChangedListenerCallback - + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId - + " events=" + Integer.toHexString(events) + "}"; + + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId + + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + "}"; } } @@ -598,9 +597,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int callerUserId = UserHandle.getCallingUserId(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); if (VDBG) { - log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId() - + " callerUserId=" + callerUserId + " callback=" + callback - + " callback.asBinder=" + callback.asBinder()); + log("listen oscl: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid() + + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId + + " callback=" + callback + " callback.asBinder=" + callback.asBinder()); } synchronized (mRecords) { @@ -652,9 +651,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int callerUserId = UserHandle.getCallingUserId(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); if (VDBG) { - log("listen ooscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId() - + " callerUserId=" + callerUserId + " callback=" + callback - + " callback.asBinder=" + callback.asBinder()); + log("listen ooscl: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid() + + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId + + " callback=" + callback + " callback.asBinder=" + callback.asBinder()); } synchronized (mRecords) { @@ -769,9 +768,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { IPhoneStateListener callback, int events, boolean notifyNow, int subId) { int callerUserId = UserHandle.getCallingUserId(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); - String str = "listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events) - + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId=" - + UserHandle.myUserId() + " callerUserId=" + callerUserId; + String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid() + + " events=0x" + Integer.toHexString(events) + " notifyNow=" + notifyNow + " subId=" + + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId; mListenLog.log(str); if (VDBG) { log(str); @@ -2957,4 +2956,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (info == null) return INVALID_SIM_SLOT_INDEX; return info.getSimSlotIndex(); } + + /** + * On certain build types, we should redact information by default. UID information will be + * preserved in the same log line, so no debugging capability is lost in full bug reports. + * However, privacy-constrained bug report types (e.g. connectivity) cannot display raw + * package names on user builds as it's considered an information leak. + */ + private static String pii(String packageName) { + return Build.IS_DEBUGGABLE ? packageName : "***"; + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9058ac451455..f64272bf08d1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4766,7 +4766,8 @@ public class ActivityManagerService extends IActivityManager.Stub packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED : ApplicationExitInfo.REASON_USER_REQUESTED, ApplicationExitInfo.SUBREASON_UNKNOWN, - packageName == null ? ("stop user " + userId) : ("stop " + packageName)); + (packageName == null ? ("stop user " + userId) : ("stop " + packageName)) + + " due to " + reason); didSomething |= mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId); @@ -17066,6 +17067,7 @@ public class ActivityManagerService extends IActivityManager.Stub proc.lastCachedPss = pss; proc.lastCachedSwapPss = swapPss; } + proc.mLastRss = rss; final SparseArray<Pair<Long, String>> watchUids = mMemWatchProcesses.getMap().get(proc.processName); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 59f64ac8c689..8f5fbf7431e1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2931,25 +2931,35 @@ final class ActivityManagerShellCommand extends ShellCommand { final PlatformCompat platformCompat = (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); String toggleValue = getNextArgRequired(); - if (toggleValue.equals("reset-all")) { - final String packageName = getNextArgRequired(); - pw.println("Reset all changes for " + packageName + " to default value."); - platformCompat.clearOverrides(packageName); - return 0; - } - long changeId; - String changeIdString = getNextArgRequired(); - try { - changeId = Long.parseLong(changeIdString); - } catch (NumberFormatException e) { - changeId = platformCompat.lookupChangeId(changeIdString); - } - if (changeId == -1) { - pw.println("Unknown or invalid change: '" + changeIdString + "'."); - return -1; + boolean toggleAll = false; + int targetSdkVersion = -1; + long changeId = -1; + + if (toggleValue.endsWith("-all")) { + toggleValue = toggleValue.substring(0, toggleValue.lastIndexOf("-all")); + toggleAll = true; + if (!toggleValue.equals("reset")) { + try { + targetSdkVersion = Integer.parseInt(getNextArgRequired()); + } catch (NumberFormatException e) { + pw.println("Invalid targetSdkVersion!"); + return -1; + } + } + } else { + String changeIdString = getNextArgRequired(); + try { + changeId = Long.parseLong(changeIdString); + } catch (NumberFormatException e) { + changeId = platformCompat.lookupChangeId(changeIdString); + } + if (changeId == -1) { + pw.println("Unknown or invalid change: '" + changeIdString + "'."); + return -1; + } } String packageName = getNextArgRequired(); - if (!platformCompat.isKnownChangeId(changeId)) { + if (!toggleAll && !platformCompat.isKnownChangeId(changeId)) { pw.println("Warning! Change " + changeId + " is not known yet. Enabling/disabling it" + " could have no effect."); } @@ -2958,22 +2968,49 @@ final class ActivityManagerShellCommand extends ShellCommand { try { switch (toggleValue) { case "enable": - enabled.add(changeId); - CompatibilityChangeConfig overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - pw.println("Enabled change " + changeId + " for " + packageName + "."); + if (toggleAll) { + int numChanges = platformCompat.enableTargetSdkChanges(packageName, + targetSdkVersion); + if (numChanges == 0) { + pw.println("No changes were enabled."); + return -1; + } + pw.println("Enabled " + numChanges + " changes gated by targetSdkVersion " + + targetSdkVersion + " for " + packageName + "."); + } else { + enabled.add(changeId); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Enabled change " + changeId + " for " + packageName + "."); + } return 0; case "disable": - disabled.add(changeId); - overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - pw.println("Disabled change " + changeId + " for " + packageName + "."); + if (toggleAll) { + int numChanges = platformCompat.disableTargetSdkChanges(packageName, + targetSdkVersion); + if (numChanges == 0) { + pw.println("No changes were disabled."); + return -1; + } + pw.println("Disabled " + numChanges + " changes gated by targetSdkVersion " + + targetSdkVersion + " for " + packageName + "."); + } else { + disabled.add(changeId); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Disabled change " + changeId + " for " + packageName + "."); + } return 0; case "reset": + if (toggleAll) { + platformCompat.clearOverrides(packageName); + pw.println("Reset all changes for " + packageName + " to default value."); + return 0; + } if (platformCompat.clearOverride(changeId, packageName)) { pw.println("Reset change " + changeId + " for " + packageName + " to default value."); @@ -3304,6 +3341,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>"); pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>."); pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect)."); + pw.println(" enable-all|disable-all <targetSdkVersion> <PACKAGE_NAME"); + pw.println(" Toggles all changes that are gated by <targetSdkVersion>."); pw.println(" reset-all <PACKAGE_NAME>"); pw.println(" Removes all existing overrides for all changes for "); pw.println(" <PACKAGE_NAME> (back to default behaviour)."); diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index a09aa64476c1..028a059c538a 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -129,7 +129,7 @@ public final class AppExitInfoTracker { private final ProcessMap<AppExitInfoContainer> mData; /** A pool of raw {@link android.app.ApplicationExitInfo} records. */ - @GuardedBy("mService") + @GuardedBy("mLock") private final SynchronizedPool<ApplicationExitInfo> mRawRecordsPool; /** @@ -204,8 +204,7 @@ public final class AppExitInfoTracker { }); } - @GuardedBy("mService") - void scheduleNoteProcessDiedLocked(final ProcessRecord app) { + void scheduleNoteProcessDied(final ProcessRecord app) { if (app == null || app.info == null) { return; } @@ -214,11 +213,9 @@ public final class AppExitInfoTracker { if (!mAppExitInfoLoaded) { return; } + mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecordLocked(app)) + .sendToTarget(); } - // The current thread is holding the global lock, let's extract the info from it - // and schedule the info note task in the kill handler. - mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecordLocked(app)) - .sendToTarget(); } void scheduleNoteAppKill(final ProcessRecord app, final @Reason int reason, @@ -227,8 +224,6 @@ public final class AppExitInfoTracker { if (!mAppExitInfoLoaded) { return; } - } - synchronized (mService) { if (app == null || app.info == null) { return; } @@ -247,8 +242,6 @@ public final class AppExitInfoTracker { if (!mAppExitInfoLoaded) { return; } - } - synchronized (mService) { ProcessRecord app; synchronized (mService.mPidsSelfLocked) { app = mService.mPidsSelfLocked.get(pid); @@ -512,9 +505,13 @@ public final class AppExitInfoTracker { @VisibleForTesting void onPackageRemoved(String packageName, int uid, boolean allUsers) { if (packageName != null) { - mAppExitInfoSourceZygote.removeByUid(uid, allUsers); - mAppExitInfoSourceLmkd.removeByUid(uid, allUsers); - mIsolatedUidRecords.removeAppUid(uid, allUsers); + final boolean removeUid = TextUtils.isEmpty( + mService.mPackageManagerInt.getNameForUid(uid)); + if (removeUid) { + mAppExitInfoSourceZygote.removeByUid(uid, allUsers); + mAppExitInfoSourceLmkd.removeByUid(uid, allUsers); + mIsolatedUidRecords.removeAppUid(uid, allUsers); + } removePackage(packageName, allUsers ? UserHandle.USER_ALL : UserHandle.getUserId(uid)); schedulePersistProcessExitInfo(true); } @@ -540,6 +537,11 @@ public final class AppExitInfoTracker { mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + boolean replacing = intent.getBooleanExtra( + Intent.EXTRA_REPLACING, false); + if (replacing) { + return; + } int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL); boolean allUsers = intent.getBooleanExtra( Intent.EXTRA_REMOVED_FOR_ALL_USERS, false); @@ -823,7 +825,7 @@ public final class AppExitInfoTracker { } @VisibleForTesting - @GuardedBy("mService") + @GuardedBy("mLock") ApplicationExitInfo obtainRawRecordLocked(ProcessRecord app) { ApplicationExitInfo info = mRawRecordsPool.acquire(); if (info == null) { @@ -842,15 +844,15 @@ public final class AppExitInfoTracker { info.setReason(ApplicationExitInfo.REASON_UNKNOWN); info.setStatus(0); info.setImportance(procStateToImportance(app.setProcState)); - info.setPss(app.lastMemInfo == null ? 0 : app.lastMemInfo.getTotalPss()); - info.setRss(app.lastMemInfo == null ? 0 : app.lastMemInfo.getTotalRss()); + info.setPss(app.lastPss); + info.setRss(app.mLastRss); info.setTimestamp(System.currentTimeMillis()); return info; } @VisibleForTesting - @GuardedBy("mService") + @GuardedBy("mLock") void recycleRawRecordLocked(ApplicationExitInfo info) { info.setProcessName(null); info.setDescription(null); @@ -1135,8 +1137,6 @@ public final class AppExitInfoTracker { ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj; synchronized (mLock) { handleNoteProcessDiedLocked(raw); - } - synchronized (mService) { recycleRawRecordLocked(raw); } } @@ -1145,8 +1145,6 @@ public final class AppExitInfoTracker { ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj; synchronized (mLock) { handleNoteAppKillLocked(raw); - } - synchronized (mService) { recycleRawRecordLocked(raw); } } diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 3a6065e18ea5..86d9028f53dc 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -121,6 +121,7 @@ public final class CachedAppOptimizer { static final int COMPACT_PROCESS_MSG = 1; static final int COMPACT_SYSTEM_MSG = 2; static final int SET_FROZEN_PROCESS_MSG = 3; + static final int REPORT_UNFREEZE_MSG = 4; //TODO:change this static definition into a configurable flag. static final int FREEZE_TIMEOUT_MS = 500; @@ -613,30 +614,6 @@ public final class CachedAppOptimizer { FREEZE_TIMEOUT_MS); } - private final class UnfreezeStats { - final int mPid; - final String mName; - final long mFrozenDuration; - - UnfreezeStats(int pid, String name, long frozenDuration) { - mPid = pid; - mName = name; - mFrozenDuration = frozenDuration; - } - - public int getPid() { - return mPid; - } - - public String getName() { - return mName; - } - - public long getFrozenDuration() { - return mFrozenDuration; - } - } - @GuardedBy("mAm") void unfreezeAppLocked(ProcessRecord app) { mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app); @@ -667,12 +644,11 @@ public final class CachedAppOptimizer { Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName); } - UnfreezeStats stats = new UnfreezeStats(app.pid, app.processName, - app.freezeUnfreezeTime - freezeTime); - mFreezeHandler.sendMessage( - mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, REPORT_UNFREEZE, 0, - stats)); + mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG, + app.pid, + (int) Math.min(app.freezeUnfreezeTime - freezeTime, Integer.MAX_VALUE), + app.processName)); } } @@ -945,14 +921,19 @@ public final class CachedAppOptimizer { @Override public void handleMessage(Message msg) { - if (msg.what != SET_FROZEN_PROCESS_MSG) { - return; - } + switch (msg.what) { + case SET_FROZEN_PROCESS_MSG: + freezeProcess((ProcessRecord) msg.obj); + break; + case REPORT_UNFREEZE_MSG: + int pid = msg.arg1; + int frozenDuration = msg.arg2; + String processName = (String) msg.obj; - if (msg.arg1 == DO_FREEZE) { - freezeProcess((ProcessRecord) msg.obj); - } else if (msg.arg1 == REPORT_UNFREEZE) { - reportUnfreeze((UnfreezeStats) msg.obj); + reportUnfreeze(pid, frozenDuration, processName); + break; + default: + return; } } @@ -1015,18 +996,18 @@ public final class CachedAppOptimizer { } } - private void reportUnfreeze(UnfreezeStats stats) { + private void reportUnfreeze(int pid, int frozenDuration, String processName) { - EventLog.writeEvent(EventLogTags.AM_UNFREEZE, stats.getPid(), stats.getName()); + EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, processName); // See above for why we're not taking mPhenotypeFlagLock here if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { FrameworkStatsLog.write( FrameworkStatsLog.APP_FREEZE_CHANGED, FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__UNFREEZE_APP, - stats.getPid(), - stats.getName(), - stats.getFrozenDuration()); + pid, + processName, + frozenDuration); } } } diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS index 7cc2e8eb2954..1f826b5253bd 100644 --- a/services/core/java/com/android/server/am/OWNERS +++ b/services/core/java/com/android/server/am/OWNERS @@ -16,12 +16,6 @@ jji@google.com # Windows & Activities ogunwale@google.com -jjaggi@google.com -racarr@google.com -chaviw@google.com -vishnun@google.com -akulian@google.com -roosa@google.com # Permissions & Packages svetoslavganov@google.com diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 644f0f776387..b584ea5b7a25 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1708,11 +1708,6 @@ public final class OomAdjuster { // TOP process passes all capabilities to the service. capability |= PROCESS_CAPABILITY_ALL; } - } else if (clientProcState - <= PROCESS_STATE_FOREGROUND_SERVICE) { - if (cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { - clientProcState = PROCESS_STATE_FOREGROUND_SERVICE; - } } } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) { if (clientProcState < @@ -2036,7 +2031,7 @@ public final class OomAdjuster { case PROCESS_STATE_TOP: return PROCESS_CAPABILITY_ALL; case PROCESS_STATE_BOUND_TOP: - return PROCESS_CAPABILITY_ALL_IMPLICIT; + return PROCESS_CAPABILITY_NONE; case PROCESS_STATE_FOREGROUND_SERVICE: if (app.hasForegroundServices()) { // Capability from FGS are conditional depending on foreground service type in @@ -2044,10 +2039,12 @@ public final class OomAdjuster { return PROCESS_CAPABILITY_NONE; } else { // process has no FGS, the PROCESS_STATE_FOREGROUND_SERVICE is from client. + // the implicit capability could be removed in the future, client should use + // BIND_INCLUDE_CAPABILITY flag. return PROCESS_CAPABILITY_ALL_IMPLICIT; } case PROCESS_STATE_BOUND_FOREGROUND_SERVICE: - return PROCESS_CAPABILITY_ALL_IMPLICIT; + return PROCESS_CAPABILITY_NONE; default: return PROCESS_CAPABILITY_NONE; } @@ -2588,8 +2585,13 @@ public final class OomAdjuster { return; } + // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze + if (app.frozen && app.shouldNotFreeze) { + mCachedAppOptimizer.unfreezeAppLocked(app); + } + // Use current adjustment when freezing, set adjustment when unfreezing. - if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && !app.frozen) { + if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && !app.frozen && !app.shouldNotFreeze) { mCachedAppOptimizer.freezeAppAsync(app); } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ && app.frozen) { mCachedAppOptimizer.unfreezeAppLocked(app); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index a9d18e05e350..6b165139aefd 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -3773,7 +3773,7 @@ public final class ProcessList { } Watchdog.getInstance().processDied(app.processName, app.pid); - mAppExitInfoTracker.scheduleNoteProcessDiedLocked(app); + mAppExitInfoTracker.scheduleNoteProcessDied(app); } /** diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index f2ca1daec306..c0298110580e 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -323,6 +323,8 @@ class ProcessRecord implements WindowProcessListener { // set of disabled compat changes for the process (all others are enabled) long[] mDisabledCompatChanges; + long mLastRss; // Last computed memory rss. + // The precede instance of the process, which would exist when the previous process is killed // but not fully dead yet; in this case, the new instance of the process should be held until // this precede instance is fully dead. @@ -431,6 +433,7 @@ class ProcessRecord implements WindowProcessListener { pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, lastSwapPss*1024); pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, lastCachedPss*1024); pw.print(" lastCachedSwapPss="); DebugUtils.printSizeValue(pw, lastCachedSwapPss*1024); + pw.print(" lastRss="); DebugUtils.printSizeValue(pw, mLastRss * 1024); pw.println(); pw.print(prefix); pw.print("procStateMemTracker: "); procStateMemTracker.dumpLine(pw); diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 7774633fa1be..471c97b0e7c6 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -40,6 +40,7 @@ import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OpEventProxyInfo; +import static android.app.AppOpsManager.RestrictionBypass; import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED; import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM; import static android.app.AppOpsManager.UID_STATE_BACKGROUND; @@ -55,6 +56,7 @@ import static android.app.AppOpsManager.extractFlagsFromKey; import static android.app.AppOpsManager.extractUidStateFromKey; import static android.app.AppOpsManager.makeKey; import static android.app.AppOpsManager.modeToName; +import static android.app.AppOpsManager.opAllowSystemBypassRestriction; import static android.app.AppOpsManager.opToName; import static android.app.AppOpsManager.opToPublicName; import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState; @@ -73,7 +75,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManager.HistoricalOps; @@ -92,8 +93,6 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -666,15 +665,19 @@ public class AppOpsService extends IAppOpsService.Stub { final static class Ops extends SparseArray<Op> { final String packageName; final UidState uidState; - final boolean isPrivileged; + + /** + * The restriction properties of the package. If {@code null} it could not have been read + * yet and has to be refreshed. + */ + @Nullable RestrictionBypass bypass; /** Lazily populated cache of featureIds of this package */ final @NonNull ArraySet<String> knownFeatureIds = new ArraySet<>(); - Ops(String _packageName, UidState _uidState, boolean _isPrivileged) { + Ops(String _packageName, UidState _uidState) { packageName = _packageName; uidState = _uidState; - isPrivileged = _isPrivileged; } } @@ -1519,7 +1522,11 @@ public class AppOpsService extends IAppOpsService.Stub { return; } + // Reset cached package properties to re-initialize when needed + ops.bypass = null; ops.knownFeatureIds.clear(); + + // Merge data collected for removed features into their successor features int numOps = ops.size(); for (int opNum = 0; opNum < numOps; opNum++) { Op op = ops.valueAt(opNum); @@ -1953,8 +1960,7 @@ public class AppOpsService extends IAppOpsService.Stub { return Collections.emptyList(); } synchronized (this) { - Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false /* isPrivileged */, - false /* edit */); + Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false /* edit */); if (pkgOps == null) { return null; } @@ -2087,8 +2093,7 @@ public class AppOpsService extends IAppOpsService.Stub { op.removeFeaturesWithNoTime(); if (op.mFeatures.size() == 0) { - Ops ops = getOpsRawLocked(uid, packageName, null, false /* isPrivileged */, - false /* edit */); + Ops ops = getOpsLocked(uid, packageName, null, null, false /* edit */); if (ops != null) { ops.remove(op.op); if (ops.size() <= 0) { @@ -2390,9 +2395,9 @@ public class AppOpsService extends IAppOpsService.Stub { ArraySet<ModeCallback> repCbs = null; code = AppOpsManager.opToSwitch(code); - boolean isPrivileged; + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null); + bypass = verifyAndGetBypass(uid, packageName, null); } catch (SecurityException e) { Slog.e(TAG, "Cannot setMode", e); return; @@ -2400,7 +2405,7 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { UidState uidState = getUidStateLocked(uid, false); - Op op = getOpLocked(code, uid, packageName, null, isPrivileged, true); + Op op = getOpLocked(code, uid, packageName, null, bypass, true); if (op != null) { if (op.mode != mode) { op.mode = mode; @@ -2798,10 +2803,9 @@ public class AppOpsService extends IAppOpsService.Stub { */ private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName, boolean raw) { - boolean isPrivileged; - + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null); + bypass = verifyAndGetBypass(uid, packageName, null); } catch (SecurityException e) { Slog.e(TAG, "checkOperation", e); return AppOpsManager.opToDefaultMode(code); @@ -2811,7 +2815,7 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } synchronized (this) { - if (isOpRestrictedLocked(uid, code, packageName, null, isPrivileged)) { + if (isOpRestrictedLocked(uid, code, packageName, bypass)) { return AppOpsManager.MODE_IGNORED; } code = AppOpsManager.opToSwitch(code); @@ -2821,7 +2825,7 @@ public class AppOpsService extends IAppOpsService.Stub { final int rawMode = uidState.opModes.get(code); return raw ? rawMode : uidState.evalMode(code, rawMode); } - Op op = getOpLocked(code, uid, packageName, null, false, false); + Op op = getOpLocked(code, uid, packageName, null, bypass, false); if (op == null) { return AppOpsManager.opToDefaultMode(code); } @@ -2884,7 +2888,7 @@ public class AppOpsService extends IAppOpsService.Stub { public int checkPackage(int uid, String packageName) { Objects.requireNonNull(packageName); try { - verifyAndGetIsPrivileged(uid, packageName, null); + verifyAndGetBypass(uid, packageName, null); return AppOpsManager.MODE_ALLOWED; } catch (SecurityException ignored) { @@ -2961,17 +2965,16 @@ public class AppOpsService extends IAppOpsService.Stub { @Nullable String featureId, int proxyUid, String proxyPackageName, @Nullable String proxyFeatureId, @OpFlags int flags, boolean shouldCollectAsyncNotedOp, @Nullable String message) { - boolean isPrivileged; + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); + bypass = verifyAndGetBypass(uid, packageName, featureId); } catch (SecurityException e) { Slog.e(TAG, "noteOperation", e); return AppOpsManager.MODE_ERRORED; } synchronized (this) { - final Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged, - true /* edit */); + final Ops ops = getOpsLocked(uid, packageName, featureId, bypass, true /* edit */); if (ops == null) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); @@ -2981,7 +2984,7 @@ public class AppOpsService extends IAppOpsService.Stub { } final Op op = getOpLocked(ops, code, uid, true); final FeatureOp featureOp = op.getOrCreateFeature(op, featureId); - if (isOpRestrictedLocked(uid, code, packageName, featureId, isPrivileged)) { + if (isOpRestrictedLocked(uid, code, packageName, bypass)) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; @@ -3205,7 +3208,7 @@ public class AppOpsService extends IAppOpsService.Stub { int uid = Binder.getCallingUid(); Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid); - verifyAndGetIsPrivileged(uid, packageName, null); + verifyAndGetBypass(uid, packageName, null); synchronized (this) { RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key); @@ -3235,7 +3238,7 @@ public class AppOpsService extends IAppOpsService.Stub { int uid = Binder.getCallingUid(); Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid); - verifyAndGetIsPrivileged(uid, packageName, null); + verifyAndGetBypass(uid, packageName, null); synchronized (this) { RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key); @@ -3254,7 +3257,7 @@ public class AppOpsService extends IAppOpsService.Stub { int uid = Binder.getCallingUid(); - verifyAndGetIsPrivileged(uid, packageName, null); + verifyAndGetBypass(uid, packageName, null); synchronized (this) { return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid)); @@ -3272,16 +3275,16 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } - boolean isPrivileged; + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); + bypass = verifyAndGetBypass(uid, packageName, featureId); } catch (SecurityException e) { Slog.e(TAG, "startOperation", e); return AppOpsManager.MODE_ERRORED; } synchronized (this) { - final Ops ops = getOpsRawLocked(uid, resolvedPackageName, featureId, isPrivileged, + final Ops ops = getOpsLocked(uid, resolvedPackageName, featureId, bypass, true /* edit */); if (ops == null) { if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid @@ -3289,7 +3292,7 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, uid, true); - if (isOpRestrictedLocked(uid, code, resolvedPackageName, featureId, isPrivileged)) { + if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) { return AppOpsManager.MODE_IGNORED; } final FeatureOp featureOp = op.getOrCreateFeature(op, featureId); @@ -3347,16 +3350,16 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - boolean isPrivileged; + RestrictionBypass bypass; try { - isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); + bypass = verifyAndGetBypass(uid, packageName, featureId); } catch (SecurityException e) { Slog.e(TAG, "Cannot finishOperation", e); return; } synchronized (this) { - Op op = getOpLocked(code, uid, resolvedPackageName, featureId, isPrivileged, true); + Op op = getOpLocked(code, uid, resolvedPackageName, featureId, bypass, true); if (op == null) { return; } @@ -3617,7 +3620,22 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * Verify that package belongs to uid and return whether the package is privileged. + * Create a restriction description matching the properties of the package. + * + * @param context A context to use + * @param pkg The package to create the restriction description for + * + * @return The restriction matching the package + */ + private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) { + return new RestrictionBypass(pkg.isPrivileged(), mContext.checkPermission( + android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid()) + == PackageManager.PERMISSION_GRANTED); + } + + /** + * Verify that package belongs to uid and return the {@link RestrictionBypass bypass + * description} for the package. * * @param uid The uid the package belongs to * @param packageName The package the might belong to the uid @@ -3625,11 +3643,11 @@ public class AppOpsService extends IAppOpsService.Stub { * * @return {@code true} iff the package is privileged */ - private boolean verifyAndGetIsPrivileged(int uid, String packageName, + private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName, @Nullable String featureId) { if (uid == Process.ROOT_UID) { // For backwards compatibility, don't check package name for root UID. - return false; + return null; } // Do not check if uid/packageName/featureId is already known @@ -3638,13 +3656,14 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState != null && uidState.pkgOps != null) { Ops ops = uidState.pkgOps.get(packageName); - if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))) { - return ops.isPrivileged; + if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId)) + && ops.bypass != null) { + return ops.bypass; } } } - boolean isPrivileged = false; + RestrictionBypass bypass = null; final long ident = Binder.clearCallingIdentity(); try { int pkgUid; @@ -3668,14 +3687,14 @@ public class AppOpsService extends IAppOpsService.Stub { pkgUid = UserHandle.getUid( UserHandle.getUserId(uid), UserHandle.getAppId(pkg.getUid())); - isPrivileged = pkg.isPrivileged(); + bypass = getBypassforPackage(pkg); } else { // Allow any feature id for resolvable uids isFeatureIdValid = true; pkgUid = resolveUid(packageName); if (pkgUid >= 0) { - isPrivileged = false; + bypass = RestrictionBypass.UNRESTRICTED; } } if (pkgUid != uid) { @@ -3692,7 +3711,7 @@ public class AppOpsService extends IAppOpsService.Stub { Binder.restoreCallingIdentity(ident); } - return isPrivileged; + return bypass; } /** @@ -3701,13 +3720,13 @@ public class AppOpsService extends IAppOpsService.Stub { * @param uid The uid the package belongs to * @param packageName The name of the package * @param featureId The feature in the package - * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false) + * @param bypass When to bypass certain op restrictions (can be null if edit == false) * @param edit If an ops does not exist, create the ops? - * @return + * @return The ops */ - private Ops getOpsRawLocked(int uid, String packageName, @Nullable String featureId, - boolean isPrivileged, boolean edit) { + private Ops getOpsLocked(int uid, String packageName, @Nullable String featureId, + @Nullable RestrictionBypass bypass, boolean edit) { UidState uidState = getUidStateLocked(uid, edit); if (uidState == null) { return null; @@ -3725,53 +3744,18 @@ public class AppOpsService extends IAppOpsService.Stub { if (!edit) { return null; } - ops = new Ops(packageName, uidState, isPrivileged); + ops = new Ops(packageName, uidState); uidState.pkgOps.put(packageName, ops); } - if (edit && featureId != null) { - ops.knownFeatureIds.add(featureId); - } - return ops; - } - - /** - * Get the state of all ops for a package. - * - * <p>Usually callers should use {@link #getOpLocked} and not call this directly. - * - * @param uid The uid the of the package - * @param packageName The package name for which to get the state for - * @param featureId The feature in the package - * @param edit Iff {@code true} create the {@link Ops} object if not yet created - * @param isPrivileged Whether the package is privileged or not - * - * @return The {@link Ops state} of all ops for the package - */ - private @Nullable Ops getOpsRawNoVerifyLocked(int uid, @NonNull String packageName, - @Nullable String featureId, boolean edit, boolean isPrivileged) { - UidState uidState = getUidStateLocked(uid, edit); - if (uidState == null) { - return null; - } - if (uidState.pkgOps == null) { - if (!edit) { - return null; + if (edit) { + if (bypass != null) { + ops.bypass = bypass; } - uidState.pkgOps = new ArrayMap<>(); - } - Ops ops = uidState.pkgOps.get(packageName); - if (ops == null) { - if (!edit) { - return null; + if (featureId != null) { + ops.knownFeatureIds.add(featureId); } - ops = new Ops(packageName, uidState, isPrivileged); - uidState.pkgOps.put(packageName, ops); - } - - if (edit && featureId != null) { - ops.knownFeatureIds.add(featureId); } return ops; @@ -3800,15 +3784,14 @@ public class AppOpsService extends IAppOpsService.Stub { * @param uid The uid the of the package * @param packageName The package name for which to get the state for * @param featureId The feature in the package - * @param isPrivileged Whether the package is privileged or not (only used if {@code edit - * == true}) + * @param bypass When to bypass certain op restrictions (can be null if edit == false) * @param edit Iff {@code true} create the {@link Op} object if not yet created * * @return The {@link Op state} of the op */ private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, - @Nullable String featureId, boolean isPrivileged, boolean edit) { - Ops ops = getOpsRawNoVerifyLocked(uid, packageName, featureId, edit, isPrivileged); + @Nullable String featureId, @Nullable RestrictionBypass bypass, boolean edit) { + Ops ops = getOpsLocked(uid, packageName, featureId, bypass, edit); if (ops == null) { return null; } @@ -3839,7 +3822,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private boolean isOpRestrictedLocked(int uid, int code, String packageName, - @Nullable String featureId, boolean isPrivileged) { + @Nullable RestrictionBypass appBypass) { int userHandle = UserHandle.getUserId(uid); final int restrictionSetCount = mOpUserRestrictions.size(); @@ -3848,12 +3831,15 @@ public class AppOpsService extends IAppOpsService.Stub { // package is exempt from the restriction. ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); if (restrictionState.hasRestriction(code, packageName, userHandle)) { - if (AppOpsManager.opAllowSystemBypassRestriction(code)) { + RestrictionBypass opBypass = opAllowSystemBypassRestriction(code); + if (opBypass != null) { // If we are the system, bypass user restrictions for certain codes synchronized (this) { - Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged, - true /* edit */); - if ((ops != null) && ops.isPrivileged) { + if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) { + return false; + } + if (opBypass.isRecordAudioRestrictionExcept && appBypass != null + && appBypass.isRecordAudioRestrictionExcept) { return false; } } @@ -4043,28 +4029,6 @@ public class AppOpsService extends IAppOpsService.Stub { throws NumberFormatException, XmlPullParserException, IOException { int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); final UidState uidState = getUidStateLocked(uid, true); - String isPrivilegedString = parser.getAttributeValue(null, "p"); - boolean isPrivileged = false; - if (isPrivilegedString == null) { - try { - IPackageManager packageManager = ActivityThread.getPackageManager(); - if (packageManager != null) { - ApplicationInfo appInfo = ActivityThread.getPackageManager() - .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid)); - if (appInfo != null) { - isPrivileged = (appInfo.privateFlags - & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; - } - } else { - // Could not load data, don't add to cache so it will be loaded later. - return; - } - } catch (RemoteException e) { - Slog.w(TAG, "Could not contact PackageManager", e); - } - } else { - isPrivileged = Boolean.parseBoolean(isPrivilegedString); - } int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -4074,7 +4038,7 @@ public class AppOpsService extends IAppOpsService.Stub { } String tagName = parser.getName(); if (tagName.equals("op")) { - readOp(parser, uidState, pkgName, isPrivileged); + readOp(parser, uidState, pkgName); } else { Slog.w(TAG, "Unknown element under <pkg>: " + parser.getName()); @@ -4108,8 +4072,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void readOp(XmlPullParser parser, @NonNull UidState uidState, - @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException, + private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName) + throws NumberFormatException, XmlPullParserException, IOException { int opCode = Integer.parseInt(parser.getAttributeValue(null, "n")); if (isIgnoredAppOp(opCode)) { @@ -4143,7 +4107,7 @@ public class AppOpsService extends IAppOpsService.Stub { } Ops ops = uidState.pkgOps.get(pkgName); if (ops == null) { - ops = new Ops(pkgName, uidState, isPrivileged); + ops = new Ops(pkgName, uidState); uidState.pkgOps.put(pkgName, ops); } ops.put(op.op, op); @@ -4235,17 +4199,6 @@ public class AppOpsService extends IAppOpsService.Stub { } out.startTag(null, "uid"); out.attribute(null, "n", Integer.toString(pkg.getUid())); - synchronized (this) { - Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), null, - false /* isPrivileged */, false /* edit */); - // Should always be present as the list of PackageOps is generated - // from Ops. - if (ops != null) { - out.attribute(null, "p", Boolean.toString(ops.isPrivileged)); - } else { - out.attribute(null, "p", Boolean.toString(false)); - } - } List<AppOpsManager.OpEntry> ops = pkg.getOps(); for (int j=0; j<ops.size(); j++) { AppOpsManager.OpEntry op = ops.get(j); @@ -5574,7 +5527,7 @@ public class AppOpsService extends IAppOpsService.Stub { } // TODO moltmann: Allow to check for feature op activeness synchronized (AppOpsService.this) { - Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false, false); + Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false); if (pkgOps == null) { return false; } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index bfc2f82d586d..b2ea311bf999 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -219,6 +219,19 @@ final class CompatConfig { } /** + * Returns whether the change is marked as disabled. + */ + boolean isDisabled(long changeId) { + synchronized (mChanges) { + CompatChange c = mChanges.get(changeId); + if (c == null) { + return false; + } + return c.getDisabled(); + } + } + + /** * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This * restores the default behaviour for the given change and app, once any app processes have been * restarted. @@ -301,6 +314,63 @@ final class CompatConfig { } } + private long[] getAllowedChangesAfterTargetSdkForPackage(String packageName, + int targetSdkVersion) + throws RemoteException { + LongArray allowed = new LongArray(); + synchronized (mChanges) { + for (int i = 0; i < mChanges.size(); ++i) { + try { + CompatChange change = mChanges.valueAt(i); + if (change.getEnableAfterTargetSdk() != targetSdkVersion) { + continue; + } + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(change.getId(), + packageName); + if (allowedState.state == OverrideAllowedState.ALLOWED) { + allowed.add(change.getId()); + } + } catch (RemoteException e) { + // Should never occur, since validator is in the same process. + throw new RuntimeException("Unable to call override validator!", e); + } + } + } + return allowed.toArray(); + } + + /** + * Enables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for + * {@param packageName}. + * + * @return The number of changes that were toggled. + */ + int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) + throws RemoteException { + long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion); + for (long changeId : changes) { + addOverride(changeId, packageName, true); + } + return changes.length; + } + + + /** + * Disables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for + * {@param packageName}. + * + * @return The number of changes that were toggled. + */ + int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) + throws RemoteException { + long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion); + for (long changeId : changes) { + addOverride(changeId, packageName, false); + } + return changes.length; + } + boolean registerListener(long changeId, CompatChange.ChangeListener listener) { boolean alreadyKnown = true; synchronized (mChanges) { diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java index 9e18c74265d7..f5d6e5ac46e5 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -59,6 +59,7 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild(); boolean finalBuild = mAndroidBuildClassifier.isFinalBuild(); int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId); + boolean disabled = mCompatConfig.isDisabled(changeId); // Allow any override for userdebug or eng builds. if (debuggableBuild) { @@ -83,12 +84,12 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { if (!finalBuild) { return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); } - // Do not allow overriding non-target sdk gated changes on user builds - if (minTargetSdk == -1) { + // Do not allow overriding default enabled changes on user builds + if (minTargetSdk == -1 && !disabled) { return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk); } // Only allow to opt-in for a targetSdk gated change. - if (applicationInfo.targetSdkVersion < minTargetSdk) { + if (disabled || applicationInfo.targetSdkVersion < minTargetSdk) { return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); } return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk); diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 821653af301d..8519b00237aa 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -166,6 +166,26 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + public int enableTargetSdkChanges(String packageName, int targetSdkVersion) + throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); + int numChanges = mCompatConfig.enableTargetSdkChangesForPackage(packageName, + targetSdkVersion); + killPackage(packageName); + return numChanges; + } + + @Override + public int disableTargetSdkChanges(String packageName, int targetSdkVersion) + throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); + int numChanges = mCompatConfig.disableTargetSdkChangesForPackage(packageName, + targetSdkVersion); + killPackage(packageName); + return numChanges; + } + + @Override public void clearOverrides(String packageName) throws RemoteException, SecurityException { checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 23b954c03cf3..2f047157d4aa 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -160,10 +160,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Whether a captive portal was found during the last network validation attempt. public boolean lastCaptivePortalDetected; - // Indicates the captive portal app was opened to show a login UI to the user, but the network - // has not validated yet. - public volatile boolean captivePortalValidationPending; - // Set to true when partial connectivity was detected. public boolean partialConnectivity; @@ -638,7 +634,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} " + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " - + "captivePortalValidationPending{" + captivePortalValidationPending + "} " + "partialConnectivity{" + partialConnectivity + "} " + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} " + "clat{" + clatd + "} " diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 25c761ab80ec..0925de8f9577 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -51,7 +51,6 @@ public class NetworkNotificationManager { LOST_INTERNET(SystemMessage.NOTE_NETWORK_LOST_INTERNET), NETWORK_SWITCH(SystemMessage.NOTE_NETWORK_SWITCH), NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET), - LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN), PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY), SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN), PRIVATE_DNS_BROKEN(SystemMessage.NOTE_NETWORK_PRIVATE_DNS_BROKEN); @@ -114,14 +113,10 @@ public class NetworkNotificationManager { } } - private static int getIcon(int transportType, NotificationType notifyType) { - if (transportType != TRANSPORT_WIFI) { - return R.drawable.stat_notify_rssi_in_range; - } - - return notifyType == NotificationType.LOGGED_IN - ? R.drawable.ic_wifi_signal_4 - : R.drawable.stat_notify_wifi_in_range; // TODO: Distinguish ! from ?. + private static int getIcon(int transportType) { + return (transportType == TRANSPORT_WIFI) + ? R.drawable.stat_notify_wifi_in_range : // TODO: Distinguish ! from ?. + R.drawable.stat_notify_rssi_in_range; } /** @@ -185,7 +180,7 @@ public class NetworkNotificationManager { Resources r = mContext.getResources(); final CharSequence title; final CharSequence details; - int icon = getIcon(transportType, notifyType); + int icon = getIcon(transportType); if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) { title = r.getString(R.string.wifi_no_internet, WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID())); @@ -235,9 +230,6 @@ public class NetworkNotificationManager { details = r.getString(R.string.network_available_sign_in_detailed, name); break; } - } else if (notifyType == NotificationType.LOGGED_IN) { - title = WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()); - details = r.getString(R.string.captive_portal_logged_in_detailed); } else if (notifyType == NotificationType.NETWORK_SWITCH) { String fromTransport = getTransportName(transportType); String toTransport = getTransportName(approximateTransportType(switchToNai)); @@ -379,7 +371,6 @@ public class NetworkNotificationManager { case NETWORK_SWITCH: return 2; case LOST_INTERNET: - case LOGGED_IN: return 1; default: return 0; diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index b456737e9fc1..2aa53cc3882e 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -60,6 +60,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private Connection mActiveConnection; private boolean mConnectionReady; + private RouteDiscoveryPreference mPendingDiscoveryPreference = null; + MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName, int userId) { super(componentName); @@ -99,6 +101,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider if (mConnectionReady) { mActiveConnection.updateDiscoveryPreference(discoveryPreference); updateBinding(); + } else { + mPendingDiscoveryPreference = discoveryPreference; } } @@ -271,6 +275,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private void onConnectionReady(Connection connection) { if (mActiveConnection == connection) { mConnectionReady = true; + if (mPendingDiscoveryPreference != null) { + updateDiscoveryPreference(mPendingDiscoveryPreference); + mPendingDiscoveryPreference = null; + } } } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index ee5a4fe248e7..4af31b036940 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -306,9 +306,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Data layer operation counters for splicing into other structures. */ private NetworkStats mUidOperations = new NetworkStats(0L, 10); - /** Must be set in factory by calling #setHandler. */ - private Handler mHandler; - private Handler.Callback mHandlerCallback; + @NonNull + private final Handler mHandler; private volatile boolean mSystemReady; private long mPersistThreshold = 2 * MB_IN_BYTES; @@ -324,6 +323,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final static int DUMP_STATS_SESSION_COUNT = 20; + @NonNull + private final Dependencies mDeps; + private static @NonNull File getDefaultSystemDir() { return new File(Environment.getDataDirectory(), "system"); } @@ -339,9 +341,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Clock.systemUTC()); } - private static final class NetworkStatsHandler extends Handler { - NetworkStatsHandler(Looper looper, Handler.Callback callback) { - super(looper, callback); + private final class NetworkStatsHandler extends Handler { + NetworkStatsHandler(@NonNull Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_PERFORM_POLL: { + performPoll(FLAG_PERSIST_ALL); + break; + } + case MSG_PERFORM_POLL_REGISTER_ALERT: { + performPoll(FLAG_PERSIST_NETWORK); + registerGlobalAlert(); + break; + } + } } } @@ -355,14 +372,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkStatsService service = new NetworkStatsService(context, networkManager, alarmManager, wakeLock, getDefaultClock(), context.getSystemService(TelephonyManager.class), new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(), - new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir()); + new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(), + new Dependencies()); service.registerLocalService(); - HandlerThread handlerThread = new HandlerThread(TAG); - Handler.Callback callback = new HandlerCallback(service); - handlerThread.start(); - Handler handler = new NetworkStatsHandler(handlerThread.getLooper(), callback); - service.setHandler(handler, callback); return service; } @@ -373,7 +386,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock, TelephonyManager teleManager, NetworkStatsSettings settings, NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir, - File baseDir) { + File baseDir, @NonNull Dependencies deps) { mContext = Objects.requireNonNull(context, "missing Context"); mNetworkManager = Objects.requireNonNull(networkManager, "missing INetworkManagementService"); @@ -387,6 +400,26 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mSystemDir = Objects.requireNonNull(systemDir, "missing systemDir"); mBaseDir = Objects.requireNonNull(baseDir, "missing baseDir"); mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists(); + mDeps = Objects.requireNonNull(deps, "missing Dependencies"); + + final HandlerThread handlerThread = mDeps.makeHandlerThread(); + handlerThread.start(); + mHandler = new NetworkStatsHandler(handlerThread.getLooper()); + } + + /** + * Dependencies of NetworkStatsService, for injection in tests. + */ + // TODO: Move more stuff into dependencies object. + @VisibleForTesting + public static class Dependencies { + /** + * Create a HandlerThread to use in NetworkStatsService. + */ + @NonNull + public HandlerThread makeHandlerThread() { + return new HandlerThread(TAG); + } } private void registerLocalService() { @@ -394,12 +427,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { new NetworkStatsManagerInternalImpl()); } - @VisibleForTesting - void setHandler(Handler handler, Handler.Callback callback) { - mHandler = handler; - mHandlerCallback = callback; - } - public void systemReady() { synchronized (mStatsLock) { mSystemReady = true; @@ -1920,33 +1947,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } - @VisibleForTesting - static class HandlerCallback implements Handler.Callback { - private final NetworkStatsService mService; - - HandlerCallback(NetworkStatsService service) { - this.mService = service; - } - - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_PERFORM_POLL: { - mService.performPoll(FLAG_PERSIST_ALL); - return true; - } - case MSG_PERFORM_POLL_REGISTER_ALERT: { - mService.performPoll(FLAG_PERSIST_NETWORK); - mService.registerGlobalAlert(); - return true; - } - default: { - return false; - } - } - } - } - private void assertSystemReady() { if (!mSystemReady) { throw new IllegalStateException("System not ready"); diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index b90681de3518..79a4da2a4a65 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -388,7 +388,7 @@ public class AppsFilter { for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) { IntentFilter intentFilter = intents.get(i); if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(), - intent.getData(), intent.getCategories(), "AppsFilter") > 0) { + intent.getData(), intent.getCategories(), "AppsFilter", true) > 0) { return true; } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 2d16854f787a..62541ab4951e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -5450,7 +5450,7 @@ public final class Settings { packagePermissions, sharedUserPermissions); } - mPersistence.writeAsUser(runtimePermissions, UserHandle.of(userId)); + mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId)); } @NonNull @@ -5504,12 +5504,12 @@ public final class Settings { } public void deleteUserRuntimePermissionsFile(int userId) { - mPersistence.deleteAsUser(UserHandle.of(userId)); + mPersistence.deleteForUser(UserHandle.of(userId)); } @GuardedBy("Settings.this.mLock") public void readStateForUserSyncLPr(int userId) { - RuntimePermissionsState runtimePermissions = mPersistence.readAsUser(UserHandle.of( + RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of( userId)); if (runtimePermissions == null) { readLegacyStateForUserSyncLPr(userId); diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index 97ce6bd7f369..b33dc8fab9a5 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -364,12 +364,12 @@ public class RoleUserState { (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked()); } - mPersistence.writeAsUser(roles, UserHandle.of(mUserId)); + mPersistence.writeForUser(roles, UserHandle.of(mUserId)); } private void readFile() { synchronized (mLock) { - RolesState roles = mPersistence.readAsUser(UserHandle.of(mUserId)); + RolesState roles = mPersistence.readForUser(UserHandle.of(mUserId)); if (roles == null) { readLegacyFileLocked(); scheduleWriteFileLocked(); @@ -545,7 +545,7 @@ public class RoleUserState { throw new IllegalStateException("This RoleUserState has already been destroyed"); } mWriteHandler.removeCallbacksAndMessages(null); - mPersistence.deleteAsUser(UserHandle.of(mUserId)); + mPersistence.deleteForUser(UserHandle.of(mUserId)); mDestroyed = true; } } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 32cff3b2eea9..98579af5835b 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -24,6 +24,8 @@ import static android.os.Debug.getIonHeapsSizeKb; import static android.os.Process.getUidForPid; import static android.os.storage.VolumeInfo.TYPE_PRIVATE; import static android.os.storage.VolumeInfo.TYPE_PUBLIC; +import static android.util.MathUtils.abs; +import static android.util.MathUtils.constrain; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; @@ -146,12 +148,15 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.MissingResourceException; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -169,6 +174,9 @@ public class StatsPullAtomService extends SystemService { private static final String TAG = "StatsPullAtomService"; private static final boolean DEBUG = true; + // Random seed stable for StatsPullAtomService life cycle - can be used for stable sampling + private static final int RANDOM_SEED = new Random().nextInt(); + /** * Lowest available uid for apps. * @@ -256,6 +264,8 @@ public class StatsPullAtomService extends SystemService { private StatsPullAtomCallbackImpl mStatsCallbackImpl; + private int mAppOpsSamplingRate = 0; + public StatsPullAtomService(Context context) { super(context); mContext = context; @@ -2877,44 +2887,7 @@ public class StatsPullAtomService extends SystemService { HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - - for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) { - final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx); - final int uid = uidOps.getUid(); - for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) { - final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx); - for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) { - final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx); - - StatsEvent.Builder e = StatsEvent.newBuilder(); - e.setAtomId(atomTag); - e.writeInt(uid); - e.writeString(packageOps.getPackageName()); - e.writeInt(op.getOpCode()); - e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED)); - e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED)); - e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED)); - e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED)); - e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED)); - e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED)); - - String perm = AppOpsManager.opToPermission(op.getOpCode()); - if (perm == null) { - e.writeBoolean(false); - } else { - PermissionInfo permInfo; - try { - permInfo = mContext.getPackageManager().getPermissionInfo(perm, 0); - e.writeBoolean(permInfo.getProtection() == PROTECTION_DANGEROUS); - } catch (PackageManager.NameNotFoundException exception) { - e.writeBoolean(false); - } - } - - pulledData.add(e.build()); - } - } - } + processHistoricalOps(histOps, atomTag, pulledData); } catch (Throwable t) { // TODO: catch exceptions at a more granular level Slog.e(TAG, "Could not read appops", t); @@ -2945,62 +2918,125 @@ public class StatsPullAtomService extends SystemService { new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).setFlags( OP_FLAGS_PULLED).build(); appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); - HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + if (mAppOpsSamplingRate == 0) { + mAppOpsSamplingRate = constrain((5000 * 100) / estimateAppOpsSize(), 1, 100); + } + processHistoricalOps(histOps, atomTag, pulledData); + } catch (Throwable t) { + // TODO: catch exceptions at a more granular level + Slog.e(TAG, "Could not read appops", t); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } - for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) { - final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx); - final int uid = uidOps.getUid(); - for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) { - final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx); + private int estimateAppOpsSize() throws Exception { + AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); + + CompletableFuture<HistoricalOps> ops = new CompletableFuture<>(); + HistoricalOpsRequest histOpsRequest = + new HistoricalOpsRequest.Builder( + Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli(), + Long.MAX_VALUE).setFlags( + OP_FLAGS_PULLED).build(); + appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); + HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, + TimeUnit.MILLISECONDS); + return processHistoricalOps(histOps, FrameworkStatsLog.APP_FEATURES_OPS, null); + } + + int processHistoricalOps(HistoricalOps histOps, int atomTag, List<StatsEvent> pulledData) { + int counter = 0; + for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) { + final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx); + final int uid = uidOps.getUid(); + for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) { + final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx); + if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { for (int featureIdx = 0; featureIdx < packageOps.getFeatureCount(); featureIdx++) { final AppOpsManager.HistoricalFeatureOps featureOps = packageOps.getFeatureOpsAt(featureIdx); for (int opIdx = 0; opIdx < featureOps.getOpCount(); opIdx++) { final AppOpsManager.HistoricalOp op = featureOps.getOpAt(opIdx); - StatsEvent.Builder e = StatsEvent.newBuilder(); - e.setAtomId(atomTag); - e.writeInt(uid); - e.writeString(packageOps.getPackageName()); - e.writeString(featureOps.getFeatureId()); - e.writeString(op.getOpName()); - e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED)); - e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED)); - e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED)); - e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED)); - e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED)); - e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED)); - - String perm = AppOpsManager.opToPermission(op.getOpCode()); - if (perm == null) { - e.writeBoolean(false); - } else { - PermissionInfo permInfo; - try { - permInfo = mContext.getPackageManager().getPermissionInfo(perm, - 0); - e.writeBoolean( - permInfo.getProtection() == PROTECTION_DANGEROUS); - } catch (PackageManager.NameNotFoundException exception) { - e.writeBoolean(false); - } - } - pulledData.add(e.build()); + counter += processHistoricalOp(op, atomTag, pulledData, uid, + packageOps.getPackageName(), featureOps.getFeatureId()); } - + } + } else if (atomTag == FrameworkStatsLog.APP_OPS) { + for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) { + final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx); + counter += processHistoricalOp(op, atomTag, pulledData, uid, + packageOps.getPackageName(), null); } } } - } catch (Throwable t) { - // TODO: catch exceptions at a more granular level - Slog.e(TAG, "Could not read appops", t); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); } - return StatsManager.PULL_SUCCESS; + return counter; + } + + private int processHistoricalOp(AppOpsManager.HistoricalOp op, int atomTag, + @Nullable List<StatsEvent> pulledData, int uid, String packageName, + @Nullable String feature) { + if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { + if (pulledData == null) { // this is size estimation call + if (op.getForegroundAccessCount(OP_FLAGS_PULLED) + op.getBackgroundAccessCount( + OP_FLAGS_PULLED) == 0) { + return 0; + } else { + return 32 + packageName.length() + (feature == null ? 1 : feature.length()); + } + } else { + if (abs((op.getOpCode() + feature + packageName).hashCode() + RANDOM_SEED) % 100 + >= mAppOpsSamplingRate) { + return 0; + } + } + } + + StatsEvent.Builder e = StatsEvent.newBuilder(); + e.setAtomId(atomTag); + e.writeInt(uid); + e.writeString(packageName); + if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { + e.writeString(feature); + } + if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { + e.writeString(op.getOpName()); + } else { + e.writeInt(op.getOpCode()); + } + e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED)); + e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED)); + e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED)); + e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED)); + e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED)); + e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED)); + + String perm = AppOpsManager.opToPermission(op.getOpCode()); + if (perm == null) { + e.writeBoolean(false); + } else { + PermissionInfo permInfo; + try { + permInfo = mContext.getPackageManager().getPermissionInfo( + perm, + 0); + e.writeBoolean( + permInfo.getProtection() == PROTECTION_DANGEROUS); + } catch (PackageManager.NameNotFoundException exception) { + e.writeBoolean(false); + } + } + if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { + e.writeInt(mAppOpsSamplingRate); + } + pulledData.add(e.build()); + return 0; } int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) { diff --git a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java index 73bb4bf956c3..948439da4863 100644 --- a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java +++ b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Binder; import android.util.EventLog; import android.util.Slog; @@ -134,7 +135,12 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { private BufferedInputStream getAltContent(Context c, Intent i) throws IOException { Uri content = getContentFromIntent(i); - return new BufferedInputStream(c.getContentResolver().openInputStream(content)); + Binder.allowBlockingForCurrentThread(); + try { + return new BufferedInputStream(c.getContentResolver().openInputStream(content)); + } finally { + Binder.defaultBlockingForCurrentThread(); + } } private byte[] getCurrentContent() { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index d7a80bfd7928..68224b59a287 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -27,7 +27,6 @@ import static com.android.server.wm.utils.RegionUtils.forEachRect; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.NonNull; -import android.app.Service; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -127,7 +126,7 @@ final class AccessibilityController { public boolean setWindowsForAccessibilityCallbackLocked(int displayId, WindowsForAccessibilityCallback callback) { if (callback != null) { - final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); + final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); if (dc == null) { return false; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 7a302110b13b..b6ad24184803 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -200,6 +200,7 @@ import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; +import static com.android.server.wm.WindowContainerChildProto.ACTIVITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE; @@ -3150,7 +3151,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Reset the last saved PiP snap fraction on removal. mDisplayContent.mPinnedStackControllerLocked.resetReentryBounds(mActivityComponent); - + mWmService.mEmbeddedWindowController.onActivityRemoved(this); mRemovingFromDisplay = false; } @@ -7450,6 +7451,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } @Override + long getProtoFieldId() { + return ACTIVITY; + } + + @Override public void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) { // Critical log level logs only visible elements to mitigate performance overheard @@ -7620,7 +7626,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return new RemoteAnimationTarget(task.mTaskId, record.getMode(), record.mAdapter.mCapturedLeash, !fillsParent(), mainWindow.mWinAnimator.mLastClipRect, insets, - getPrefixOrderIndex(), record.mAdapter.mPosition, + getPrefixOrderIndex(), record.mAdapter.mPosition, record.mAdapter.mLocalBounds, record.mAdapter.mStackBounds, task.getWindowConfiguration(), false /*isNotInRecents*/, record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null, diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 420675c5ba1e..9bad799b68d1 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -90,7 +90,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; -import static com.android.server.wm.TaskProto.ACTIVITIES; import static com.android.server.wm.TaskProto.ACTIVITY_TYPE; import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS; import static com.android.server.wm.TaskProto.BOUNDS; @@ -109,7 +108,6 @@ import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY; import static com.android.server.wm.TaskProto.ROOT_TASK_ID; import static com.android.server.wm.TaskProto.SURFACE_HEIGHT; import static com.android.server.wm.TaskProto.SURFACE_WIDTH; -import static com.android.server.wm.TaskProto.TASKS; import static com.android.server.wm.TaskProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -649,14 +647,6 @@ class ActivityStack extends Task { if (prevWindowingMode != getWindowingMode()) { mDisplayContent.onStackWindowingModeChanged(this); - - if (inSplitScreenSecondaryWindowingMode()) { - // When the stack is resized due to entering split screen secondary, offset the - // windows to compensate for the new stack position. - forAllWindows(w -> { - w.mWinAnimator.setOffsetPositionForStackResize(true); - }, true); - } } final DisplayContent display = getDisplay(); @@ -2395,8 +2385,10 @@ class ActivityStack extends Task { } Task task = null; if (!newTask && isOrhasTask) { + // Starting activity cannot be occluding activity, otherwise starting window could be + // remove immediately without transferring to starting activity. final ActivityRecord occludingActivity = getActivity( - (ar) -> !ar.finishing && ar.occludesParent(), true, rTask); + (ar) -> !ar.finishing && 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 @@ -3883,9 +3875,10 @@ class ActivityStack extends Task { return; } if (mTile != null) { - reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl()); + // don't use reparentSurfaceControl because we need to bypass taskorg check + mSurfaceAnimator.reparent(getPendingTransaction(), mTile.getSurfaceControl()); } else if (mTile == null && origTile != null) { - reparentSurfaceControl(getPendingTransaction(), getParentSurfaceControl()); + mSurfaceAnimator.reparent(getPendingTransaction(), getParentSurfaceControl()); } } @@ -3918,17 +3911,6 @@ class ActivityStack extends Task { proto.write(DISPLAY_ID, getDisplayId()); proto.write(ROOT_TASK_ID, getRootTaskId()); - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer child = mChildren.get(i); - if (child instanceof Task) { - child.dumpDebug(proto, TASKS, logLevel); - } else if (child instanceof ActivityRecord) { - child.dumpDebug(proto, ACTIVITIES, logLevel); - } else { - throw new IllegalStateException("Unknown child type: " + child); - } - } - if (mResumedActivity != null) { mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 984ae218431c..c7a139176f6c 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -29,7 +29,6 @@ import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.WaitResult.LAUNCH_STATE_COLD; import static android.app.WaitResult.LAUNCH_STATE_HOT; -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; @@ -178,6 +177,9 @@ class ActivityStarter { private Intent mNewTaskIntent; private ActivityStack mSourceStack; private ActivityStack mTargetStack; + // The task that the last activity was started into. We currently reset the actual start + // activity's task and as a result may not have a reference to the task in all cases + private Task mTargetTask; private boolean mMovedToFront; private boolean mNoAnimation; private boolean mKeepCurTransition; @@ -545,6 +547,7 @@ class ActivityStarter { mNewTaskIntent = starter.mNewTaskIntent; mSourceStack = starter.mSourceStack; + mTargetTask = starter.mTargetTask; mTargetStack = starter.mTargetStack; mMovedToFront = starter.mMovedToFront; mNoAnimation = starter.mNoAnimation; @@ -1368,7 +1371,10 @@ class ActivityStarter { // it waits for the new activity to become visible instead, {@link #waitResultIfNeeded}. mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result); - if (startedActivityStack == null) { + final Task targetTask = r.getTask() != null + ? r.getTask() + : mTargetTask; + if (startedActivityStack == null || targetTask == null) { return; } @@ -1379,19 +1385,10 @@ class ActivityStarter { // The activity was already running so it wasn't started, but either brought to the // front or the new intent was delivered to it since it was already in front. Notify // anyone interested in this piece of information. - switch (startedActivityStack.getWindowingMode()) { - case WINDOWING_MODE_PINNED: - mService.getTaskChangeNotificationController().notifyPinnedActivityRestartAttempt( - clearedTask); - break; - case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: - final ActivityStack homeStack = - startedActivityStack.getDisplay().getOrCreateRootHomeTask(); - if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) { - mService.mWindowManager.showRecentApps(); - } - break; - } + final ActivityStack homeStack = targetTask.getDisplayContent().getRootHomeTask(); + final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null); + mService.getTaskChangeNotificationController().notifyActivityRestartAttempt( + targetTask.getTaskInfo(), homeTaskVisible, clearedTask); } } @@ -1517,6 +1514,7 @@ class ActivityStarter { // Compute if there is an existing task that should be used for. final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask(); final boolean newTask = targetTask == null; + mTargetTask = targetTask; computeLaunchParams(r, sourceRecord, targetTask); @@ -1570,10 +1568,16 @@ class ActivityStarter { mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName, mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId); - mService.getPackageManagerInternalLocked().grantImplicitAccess( - mStartActivity.mUserId, mIntent, - UserHandle.getAppId(mStartActivity.info.applicationInfo.uid), mCallingUid, - true /*direct*/); + if (mStartActivity.resultTo != null && mStartActivity.resultTo.info != null) { + // we need to resolve resultTo to a uid as grantImplicitAccess deals explicitly in UIDs + final PackageManagerInternal pmInternal = + mService.getPackageManagerInternalLocked(); + final int resultToUid = pmInternal.getPackageUidInternal( + mStartActivity.resultTo.info.packageName, 0, mStartActivity.mUserId); + pmInternal.grantImplicitAccess(mStartActivity.mUserId, mIntent, + UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) /*recipient*/, + resultToUid /*visible*/, true /*direct*/); + } if (newTask) { EventLogTags.writeWmCreateTask(mStartActivity.mUserId, mStartActivity.getTask().mTaskId); @@ -2012,6 +2016,7 @@ class ActivityStarter { mSourceStack = null; mTargetStack = null; + mTargetTask = null; mMovedToFront = false; mNoAnimation = false; mKeepCurTransition = false; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index d3ff912ea327..7bacc427feb8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -288,6 +288,7 @@ import java.lang.ref.WeakReference; import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -355,6 +356,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityManagerInternal mAmInternal; UriGrantsManagerInternal mUgmInternal; private PackageManagerInternal mPmInternal; + /** The cached sys ui service component name from package manager. */ + private ComponentName mSysUiServiceComponent; private PermissionPolicyInternal mPermissionPolicyInternal; @VisibleForTesting final ActivityTaskManagerInternal mInternal; @@ -680,7 +683,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void onChange(boolean selfChange, Iterable<Uri> uris, int flags, + public void onChange(boolean selfChange, Collection<Uri> uris, int flags, @UserIdInt int userId) { for (Uri uri : uris) { if (mFontScaleUri.equals(uri)) { @@ -5868,6 +5871,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mPmInternal; } + ComponentName getSysUiServiceComponentLocked() { + if (mSysUiServiceComponent == null) { + final PackageManagerInternal pm = getPackageManagerInternalLocked(); + mSysUiServiceComponent = pm.getSystemUiServiceComponent(); + } + return mSysUiServiceComponent; + } + PermissionPolicyInternal getPermissionPolicyInternal() { if (mPermissionPolicyInternal == null) { mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class); diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index 94decc792fd3..c18ed7d588d8 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -114,7 +114,6 @@ class BLASTSyncEngine { return st.addToSync(wc); } - // TODO(b/148476626): TIMEOUTS! void setReady(int id) { final SyncState st = mPendingSyncs.get(id); st.setReady(); diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 0d365b16e228..d9e41afa9141 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -23,13 +23,10 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static com.android.internal.util.Preconditions.checkState; -import static com.android.server.wm.DisplayAreaChildProto.DISPLAY_AREA; -import static com.android.server.wm.DisplayAreaChildProto.UNKNOWN; -import static com.android.server.wm.DisplayAreaChildProto.WINDOW; -import static com.android.server.wm.DisplayAreaProto.CHILDREN; import static com.android.server.wm.DisplayAreaProto.NAME; import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; +import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; @@ -111,24 +108,14 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { final long token = proto.start(fieldId); super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); proto.write(NAME, mName); - for (int i = 0; i < getChildCount(); i++) { - final long childToken = proto.start(CHILDREN); - final T child = getChildAt(i); - if (child instanceof ActivityStack) { - // TODO(display-area): Dump stacks & tasks here, instead of in DisplayContent's - // dumpDebug. For now, skip them here to avoid dumping them as UNKNOWN. - } else if (child instanceof WindowToken) { - ((WindowToken) child).dumpDebug(proto, WINDOW, logLevel); - } else if (child instanceof DisplayArea) { - child.dumpDebug(proto, DISPLAY_AREA, logLevel); - } else { - proto.write(UNKNOWN, child.getClass().getSimpleName()); - } - proto.end(childToken); - } proto.end(token); } + @Override + long getProtoFieldId() { + return DISPLAY_AREA; + } + /** * DisplayArea that contains WindowTokens, and orders them according to their type. */ diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 55ce84eb213b..36fdb2d1a180 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -94,6 +94,7 @@ import static com.android.server.wm.DisplayContentProto.APP_TRANSITION; import static com.android.server.wm.DisplayContentProto.CLOSING_APPS; import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES; import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO; +import static com.android.server.wm.DisplayContentProto.DISPLAY_READY; import static com.android.server.wm.DisplayContentProto.DPI; import static com.android.server.wm.DisplayContentProto.FOCUSED_APP; import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID; @@ -105,7 +106,6 @@ import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA; import static com.android.server.wm.DisplayContentProto.ROTATION; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE; -import static com.android.server.wm.DisplayContentProto.TASKS; import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; @@ -117,6 +117,7 @@ import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; import static com.android.server.wm.RootWindowContainer.TAG_STATES; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; +import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; @@ -2837,10 +2838,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo proto.write(ID, mDisplayId); mRootDisplayArea.dumpDebug(proto, ROOT_DISPLAY_AREA, logLevel); - for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) { - final ActivityStack stack = mTaskContainers.getChildAt(i); - stack.dumpDebug(proto, TASKS, logLevel); - } for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) { final WindowToken windowToken = mOverlayContainers.getChildAt(i); windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel); @@ -2875,11 +2872,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } else { proto.write(FOCUSED_ROOT_TASK_ID, INVALID_TASK_ID); } + proto.write(DISPLAY_READY, isReady()); proto.end(token); } @Override + long getProtoFieldId() { + return DISPLAY_CONTENT; + } + + @Override public void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); pw.print(prefix); @@ -4931,6 +4934,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo scheduleAnimation(); } } + + @Override + boolean shouldMagnify() { + // Omitted from Screen-Magnification + return false; + } } /** diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index d5a0d05b0f84..a094c816320a 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -28,6 +28,7 @@ import static android.view.Display.TYPE_INTERNAL; import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; +import static android.view.InsetsState.ITYPE_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; @@ -1469,8 +1470,14 @@ public class DisplayPolicy { */ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { displayFrames.onBeginLayout(); - updateInsetsStateForDisplayCutout(displayFrames, - mDisplayContent.getInsetsStateController().getRawInsetsState()); + 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); mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 884f7694d648..484a5a8b53b8 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -17,11 +17,15 @@ package com.android.server.wm; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + import android.annotation.Nullable; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.Slog; import android.view.IWindow; import android.view.InputApplicationHandle; @@ -33,12 +37,15 @@ import android.view.InputApplicationHandle; * the host window to send pointerDownOutsideFocus. */ class EmbeddedWindowController { + private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM; /* maps input token to an embedded window */ private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>(); - private final Object mWmLock; + private final Object mGlobalLock; + private final ActivityTaskManagerService mAtmService; - EmbeddedWindowController(Object wmLock) { - mWmLock = wmLock; + EmbeddedWindowController(ActivityTaskManagerService atmService) { + mAtmService = atmService; + mGlobalLock = atmService.getGlobalLock(); } /** @@ -46,13 +53,14 @@ class EmbeddedWindowController { * * @param inputToken input channel token passed in by the embedding process when it requests * the server to add an input channel to the embedded surface. - * @param embeddedWindow An {@link EmbeddedWindow} object to add to this controller. + * @param window An {@link EmbeddedWindow} object to add to this controller. */ - void add(IBinder inputToken, EmbeddedWindow embeddedWindow) { + void add(IBinder inputToken, EmbeddedWindow window) { try { - mWindows.put(inputToken, embeddedWindow); - embeddedWindow.mClient.asBinder().linkToDeath(()-> { - synchronized (mWmLock) { + mWindows.put(inputToken, window); + updateProcessController(window); + window.mClient.asBinder().linkToDeath(()-> { + synchronized (mGlobalLock) { mWindows.remove(inputToken); } }, 0); @@ -62,6 +70,23 @@ class EmbeddedWindowController { } } + /** + * Track the host activity in the embedding process so we can determine if the + * process is currently showing any UI to the user. + */ + private void updateProcessController(EmbeddedWindow window) { + if (window.mHostActivityRecord == null) { + return; + } + final WindowProcessController processController = + mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid); + if (processController == null) { + Slog.w(TAG, "Could not find the embedding process."); + } else { + processController.addHostActivity(window.mHostActivityRecord); + } + } + WindowState getHostWindow(IBinder inputToken) { EmbeddedWindow embeddedWindow = mWindows.get(inputToken); return embeddedWindow != null ? embeddedWindow.mHostWindowState : null; @@ -76,7 +101,7 @@ class EmbeddedWindowController { } } - void removeWindowsWithHost(WindowState host) { + void onWindowRemoved(WindowState host) { for (int i = mWindows.size() - 1; i >= 0; i--) { if (mWindows.valueAt(i).mHostWindowState == host) { mWindows.removeAt(i); @@ -88,9 +113,23 @@ class EmbeddedWindowController { return mWindows.get(inputToken); } + void onActivityRemoved(ActivityRecord activityRecord) { + for (int i = mWindows.size() - 1; i >= 0; i--) { + final EmbeddedWindow window = mWindows.valueAt(i); + if (window.mHostActivityRecord == activityRecord) { + final WindowProcessController processController = + mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid); + if (processController != null) { + processController.removeHostActivity(activityRecord); + } + } + } + } + static class EmbeddedWindow { final IWindow mClient; @Nullable final WindowState mHostWindowState; + @Nullable final ActivityRecord mHostActivityRecord; final int mOwnerUid; final int mOwnerPid; @@ -107,6 +146,8 @@ class EmbeddedWindowController { int ownerPid) { mClient = clientToken; mHostWindowState = hostWindowState; + mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord + : null; mOwnerUid = ownerUid; mOwnerPid = ownerPid; } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 58aefdc0e547..ada6d4710967 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -274,7 +274,7 @@ class InsetsSourceProvider { // window crop of the surface controls (including the leash) until the client finishes // drawing the new frame of the new orientation. Although we cannot defer the reparent // operation, it is fine, because reparent won't cause any visual effect. - final SurfaceControl barrier = mWin.getDeferTransactionBarrier(); + final SurfaceControl barrier = mWin.getClientViewRootSurface(); t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber); t.deferTransactionUntil(leash, barrier, frameNumber); } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index e1906da3378f..6ff029bfaea1 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -18,10 +18,14 @@ package com.android.server.wm; import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; +import static android.view.InsetsState.ITYPE_INVALID; 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.ViewRootImpl.sNewInsetsMode; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,6 +36,7 @@ import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; +import android.view.WindowManager; import java.io.PrintWriter; import java.util.ArrayList; @@ -74,15 +79,40 @@ class InsetsStateController { * @param target The client we dispatch the state to. * @return The state stripped of the necessary information. */ - InsetsState getInsetsForDispatch(WindowState target) { + InsetsState getInsetsForDispatch(@NonNull WindowState target) { final InsetsSourceProvider provider = target.getControllableInsetProvider(); if (provider == null) { return mState; } + final @InternalInsetsType int type = provider.getSource().getType(); + return getInsetsForType(type); + } + + InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { + final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type); + if (type == ITYPE_INVALID) { + return mState; + } + return getInsetsForType(type); + } + + @InternalInsetsType + private static int getInsetsTypeForWindowType(int type) { + switch(type) { + case TYPE_STATUS_BAR: + return ITYPE_STATUS_BAR; + case TYPE_NAVIGATION_BAR: + return ITYPE_NAVIGATION_BAR; + case TYPE_INPUT_METHOD: + return ITYPE_IME; + default: + return ITYPE_INVALID; + } + } + private InsetsState getInsetsForType(@InternalInsetsType int type) { final InsetsState state = new InsetsState(); state.set(mState); - final int type = provider.getSource().getType(); state.removeSource(type); // Navigation bar doesn't get influenced by anything else diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 5cd016922e68..3e5cb50d6101 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -820,15 +820,19 @@ public class RecentsAnimationController implements DeathRecipient { private @AnimationType int mLastAnimationType; private final boolean mIsRecentTaskInvisible; private RemoteAnimationTarget mTarget; - private final Point mPosition = new Point(); private final Rect mBounds = new Rect(); + // The bounds of the target relative to its parent. + private Rect mLocalBounds = new Rect(); TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { mTask = task; mIsRecentTaskInvisible = isRecentTaskInvisible; - final WindowContainer container = mTask.getParent(); - mBounds.set(container.getDisplayedBounds()); - mPosition.set(mBounds.left, mBounds.top); + mBounds.set(mTask.getDisplayedBounds()); + + mLocalBounds.set(mBounds); + Point tmpPos = new Point(); + mTask.getRelativeDisplayedPosition(tmpPos); + mLocalBounds.offsetTo(tmpPos.x, tmpPos.y); } RemoteAnimationTarget createRemoteAnimationTarget() { @@ -847,8 +851,9 @@ public class RecentsAnimationController implements DeathRecipient { : MODE_CLOSING; mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash, !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect, - insets, mTask.getPrefixOrderIndex(), mPosition, mBounds, - mTask.getWindowConfiguration(), mIsRecentTaskInvisible, null, null); + insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top), + mLocalBounds, mBounds, mTask.getWindowConfiguration(), + mIsRecentTaskInvisible, null, null); return mTarget; } @@ -862,8 +867,8 @@ public class RecentsAnimationController implements DeathRecipient { @AnimationType int type, OnAnimationFinishedCallback finishCallback) { // Restore z-layering, position and stack crop until client has a chance to modify it. t.setLayer(animationLeash, mTask.getPrefixOrderIndex()); - t.setPosition(animationLeash, mPosition.x, mPosition.y); - mTmpRect.set(mBounds); + t.setPosition(animationLeash, mLocalBounds.left, mLocalBounds.top); + mTmpRect.set(mLocalBounds); mTmpRect.offsetTo(0, 0); t.setWindowCrop(animationLeash, mTmpRect); mCapturedLeash = animationLeash; @@ -897,7 +902,7 @@ public class RecentsAnimationController implements DeathRecipient { pw.print(prefix); pw.println("Target: null"); } pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); - pw.println("mPosition=" + mPosition); + pw.println("mLocalBounds=" + mLocalBounds); pw.println("mBounds=" + mBounds); pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); } diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 0eb9daf26d47..35f8d343acc1 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -80,16 +80,17 @@ class RemoteAnimationController implements DeathRecipient { * * @param windowContainer The windows to animate. * @param position The position app bounds, in screen coordinates. + * @param localBounds The bounds of the app relative to its parent. * @param stackBounds The stack bounds of the app relative to position. * @param startBounds The stack bounds before the transition, in screen coordinates * @return The record representing animation(s) to run on the app. */ RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer, - Point position, Rect stackBounds, Rect startBounds) { + Point position, Rect localBounds, Rect stackBounds, Rect startBounds) { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s", windowContainer); - final RemoteAnimationRecord adapters = - new RemoteAnimationRecord(windowContainer, position, stackBounds, startBounds); + final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position, + localBounds, stackBounds, startBounds); mPendingAnimations.add(adapters); return adapters; } @@ -355,17 +356,18 @@ class RemoteAnimationController implements DeathRecipient { final WindowContainer mWindowContainer; final Rect mStartBounds; - RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect endBounds, - Rect startBounds) { + RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds, + Rect endBounds, Rect startBounds) { mWindowContainer = windowContainer; - mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, endBounds); + mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds); if (startBounds != null) { mStartBounds = new Rect(startBounds); mTmpRect.set(startBounds); mTmpRect.offsetTo(0, 0); if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) { mThumbnailAdapter = - new RemoteAnimationAdapterWrapper(this, new Point(0, 0), mTmpRect); + new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds, + mTmpRect); } } else { mStartBounds = null; @@ -401,12 +403,14 @@ class RemoteAnimationController implements DeathRecipient { private OnAnimationFinishedCallback mCapturedFinishCallback; private @AnimationType int mAnimationType; final Point mPosition = new Point(); + final Rect mLocalBounds; final Rect mStackBounds = new Rect(); RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position, - Rect stackBounds) { + Rect localBounds, Rect stackBounds) { mRecord = record; mPosition.set(position.x, position.y); + mLocalBounds = localBounds; mStackBounds.set(stackBounds); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 0fa135b47efb..15a49a791216 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -66,11 +66,9 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; -import static com.android.server.wm.RootWindowContainerProto.DISPLAYS; import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT; import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER; import static com.android.server.wm.RootWindowContainerProto.PENDING_ACTIVITIES; -import static com.android.server.wm.RootWindowContainerProto.WINDOWS; import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER; import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT; @@ -1278,18 +1276,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final long token = proto.start(fieldId); super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); - if (mWmService.mDisplayReady) { - final int count = mChildren.size(); - for (int i = 0; i < count; ++i) { - final DisplayContent displayContent = mChildren.get(i); - displayContent.dumpDebug(proto, DISPLAYS, logLevel); - } - } - if (logLevel == WindowTraceLogLevel.ALL) { - forAllWindows((w) -> { - w.dumpDebug(proto, WINDOWS, logLevel); - }, true); - } mStackSupervisor.getKeyguardController().dumpDebug(proto, KEYGUARD_CONTROLLER); proto.write(IS_HOME_RECENTS_COMPONENT, diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java index 024da888248d..8e1c6329f39d 100644 --- a/services/core/java/com/android/server/wm/SeamlessRotator.java +++ b/services/core/java/com/android/server/wm/SeamlessRotator.java @@ -118,9 +118,9 @@ public class SeamlessRotator { finish(t, win); if (win.mWinAnimator.mSurfaceController != null && !timeout) { t.deferTransactionUntil(win.mSurfaceControl, - win.getDeferTransactionBarrier(), win.getFrameNumber()); + win.getClientViewRootSurface(), win.getFrameNumber()); t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl, - win.getDeferTransactionBarrier(), win.getFrameNumber()); + win.getClientViewRootSurface(), win.getFrameNumber()); } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 88d4fb31b16b..b2db99b06cde 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -85,6 +85,7 @@ import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; +import static com.android.server.wm.WindowContainerChildProto.TASK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -3970,12 +3971,12 @@ class Task extends WindowContainer<WindowContainer> { boolean isControlledByTaskOrganizer() { final Task rootTask = getRootTask(); - return rootTask == this && rootTask.mTaskOrganizer != null - // TODO(task-hierarchy): Figure out how to control nested tasks. - // For now, if this is in a tile let WM drive. - && !(rootTask instanceof TaskTile) - && !(rootTask instanceof ActivityStack - && ((ActivityStack) rootTask).getTile() != null); + // 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) { + return false; + } + return rootTask == this && rootTask.mTaskOrganizer != null; } @Override @@ -4121,4 +4122,9 @@ class Task extends WindowContainer<WindowContainer> { SurfaceControl.Transaction getMainWindowSizeChangeTransaction() { return mMainWindowSizeChangeTransaction; } + + @Override + long getProtoFieldId() { + return TASK; + } } diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 96a9127344e8..e4f10d9840b2 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -30,13 +30,15 @@ import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; +import com.android.internal.os.SomeArgs; + import java.util.ArrayList; class TaskChangeNotificationController { private static final int LOG_STACK_STATE_MSG = 1; private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2; private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3; - private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; + private static final int NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6; private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7; private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8; @@ -118,8 +120,10 @@ class TaskChangeNotificationController { l.onActivityUnpinned(); }; - private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> { - l.onPinnedActivityRestartAttempt(m.arg1 != 0); + private final TaskStackConsumer mNotifyActivityRestartAttempt = (l, m) -> { + SomeArgs args = (SomeArgs) m.obj; + l.onActivityRestartAttempt((RunningTaskInfo) args.arg1, args.argi1 != 0, + args.argi2 != 0); }; private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> { @@ -220,8 +224,8 @@ class TaskChangeNotificationController { case NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG: forAllRemoteListeners(mNotifyActivityUnpinned, msg); break; - case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: - forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg); + case NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: + forAllRemoteListeners(mNotifyActivityRestartAttempt, msg); break; case NOTIFY_FORCED_RESIZABLE_MSG: forAllRemoteListeners(mNotifyActivityForcedResizable, msg); @@ -266,6 +270,9 @@ class TaskChangeNotificationController { forAllRemoteListeners(mNotifyTaskFocusChanged, msg); break; } + if (msg.obj instanceof SomeArgs) { + ((SomeArgs) msg.obj).recycle(); + } } } @@ -358,15 +365,18 @@ class TaskChangeNotificationController { /** * Notifies all listeners when an attempt was made to start an an activity that is already - * running in the pinned stack and the activity was not actually started, but the task is - * either brought to the front or a new Intent is delivered to it. + * running, but the task is either brought to the front or a new Intent is delivered to it. */ - void notifyPinnedActivityRestartAttempt(boolean clearedTask) { - mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); - final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG, - clearedTask ? 1 : 0, 0); - forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg); + void notifyActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask) { + mHandler.removeMessages(NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG); + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = task; + args.argi1 = homeTaskVisible ? 1 : 0; + args.argi2 = clearedTask ? 1 : 0; + final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG, + args); + forAllLocalListeners(mNotifyActivityRestartAttempt, msg); msg.sendToTarget(); } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index b38c18bf15f3..4093fe5cb288 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -128,7 +128,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub void removeTask(Task t) { try { - mOrganizer.taskVanished(t.getRemoteToken()); + mOrganizer.taskVanished(t.getTaskInfo()); } catch (Exception e) { Slog.e(TAG, "Exception sending taskVanished callback" + e); } diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java index bd705998f49a..f46701536cf8 100644 --- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java +++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java @@ -92,7 +92,7 @@ class WallpaperAnimationAdapter implements AnimationAdapter { */ RemoteAnimationTarget createRemoteAnimationTarget() { mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, null, null, - mWallpaperToken.getPrefixOrderIndex(), new Point(), null, + mWallpaperToken.getPrefixOrderIndex(), new Point(), null, null, mWallpaperToken.getWindowConfiguration(), true, null, null); return mTarget; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 10d34b57bacb..e8897e1dbbe2 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -40,6 +40,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITIO import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; +import static com.android.server.wm.WindowContainerChildProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER; import static com.android.server.wm.WindowContainerProto.ORIENTATION; import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR; @@ -1819,9 +1820,25 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mSurfaceAnimator.isAnimating()) { mSurfaceAnimator.dumpDebug(proto, SURFACE_ANIMATOR); } + + // add children to proto + for (int i = 0; i < getChildCount(); i++) { + final long childToken = proto.start(WindowContainerProto.CHILDREN); + final E child = getChildAt(i); + child.dumpDebug(proto, child.getProtoFieldId(), logLevel); + proto.end(childToken); + } proto.end(token); } + /** + * @return a proto field id to identify where to add the derived class to the generic window + * container proto. + */ + long getProtoFieldId() { + return WINDOW_CONTAINER; + } + private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) { ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire(); if (wrapper == null) { @@ -2086,9 +2103,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // Delaying animation start isn't compatible with remote animations at all. if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) { + final Rect localBounds = new Rect(mTmpRect); + localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y); final RemoteAnimationController.RemoteAnimationRecord adapters = - controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect, - (isChanging ? mSurfaceFreezer.mFreezeBounds : null)); + controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds, + mTmpRect, (isChanging ? mSurfaceFreezer.mFreezeBounds : null)); resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter); } else if (isChanging) { final float durationScale = mWmService.getTransitionAnimationScaleLocked(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3f4f629b5292..ecbbb03a7c94 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1287,7 +1287,7 @@ public class WindowManagerService extends IWindowManager.Stub mConstants.start(new HandlerExecutor(mH)); LocalServices.addService(WindowManagerInternal.class, new LocalService()); - mEmbeddedWindowController = new EmbeddedWindowController(mGlobalLock); + mEmbeddedWindowController = new EmbeddedWindowController(mAtmService); mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources( mContext.getResources()); @@ -1900,7 +1900,7 @@ public class WindowManagerService extends IWindowManager.Stub if (dc.mCurrentFocus == null) { dc.mWinRemovedSinceNullFocus.add(win); } - mEmbeddedWindowController.removeWindowsWithHost(win); + mEmbeddedWindowController.onWindowRemoved(win); mPendingRemove.remove(win); mResizingWindows.remove(win); updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */); @@ -2447,10 +2447,6 @@ public class WindowManagerService extends IWindowManager.Stub // of a transaction to avoid artifacts. win.mAnimatingExit = true; } else { - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent.mInputMethodWindow == win) { - displayContent.setInputMethodWindowLocked(null); - } boolean stopped = win.mActivityRecord != null ? win.mActivityRecord.mAppStopped : true; // We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces // will later actually destroy the surface if we do not do so here. Normally we leave @@ -2610,7 +2606,8 @@ public class WindowManagerService extends IWindowManager.Stub if (type == TYPE_WALLPAPER) { new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens); } else { - new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens); + new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens, + false /* roundedCornerOverlay */, fromClientToken); } } } finally { @@ -4661,6 +4658,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int RECOMPUTE_FOCUS = 61; public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62; public static final int LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED = 63; + public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64; /** * Used to denote that an integer field in a message will not be used. @@ -5041,6 +5039,13 @@ public class WindowManagerService extends IWindowManager.Stub } break; } + case WINDOW_STATE_BLAST_SYNC_TIMEOUT: { + synchronized (mGlobalLock) { + final WindowState ws = (WindowState) msg.obj; + ws.finishDrawing(null); + } + break; + } } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG_WM, "handleMessage: exit"); @@ -6126,7 +6131,7 @@ public class WindowManagerService extends IWindowManager.Stub } mRoot.forAllWindows((w) -> { - if ((!visibleOnly || w.mWinAnimator.getShown()) + if ((!visibleOnly || w.isVisible()) && (!appsOnly || w.mActivityRecord != null)) { windows.add(w); } @@ -8010,9 +8015,9 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void getWindowInsets(WindowManager.LayoutParams attrs, + public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId, Rect outContentInsets, Rect outStableInsets, - DisplayCutout.ParcelableWrapper displayCutout) { + DisplayCutout.ParcelableWrapper outDisplayCutout, InsetsState outInsetsState) { final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -8022,8 +8027,13 @@ public class WindowManagerService extends IWindowManager.Stub + "could not be found!"); } final WindowToken windowToken = dc.getWindowToken(attrs.token); - dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, mTmpRect /* outFrame */, - outContentInsets, outStableInsets, displayCutout); + final InsetsStateController insetsStateController = + dc.getInsetsStateController(); + outInsetsState.set(insetsStateController.getInsetsForWindowMetrics(attrs)); + + return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, + mTmpRect /* outFrame */, outContentInsets, outStableInsets, + outDisplayCutout); } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 03dc4c9fd1d2..32eb932ea0ed 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -41,6 +41,7 @@ import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_T import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread; import android.app.IApplicationThread; import android.app.ProfilerInfo; @@ -52,6 +53,7 @@ import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.os.Build; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.util.ArraySet; @@ -185,6 +187,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // Registered display id as a listener to override config change private int mDisplayId; private ActivityRecord mConfigActivityRecord; + /** + * Activities that hosts some UI drawn by the current process. The activities live + * in another process. This is used to check if the process is currently showing anything + * visible to the user. + */ + @Nullable + private final ArrayList<ActivityRecord> mHostActivities = new ArrayList<>(); /** Whether our process is currently running a {@link RecentsAnimation} */ private boolean mRunningRecentsAnimation; @@ -192,6 +201,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Whether our process is currently running a {@link IRemoteAnimationRunner} */ private boolean mRunningRemoteAnimation; + /** Whether this process is owned by the System UI package. */ + final boolean mIsSysUiPackage; + public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info, String name, int uid, int userId, Object owner, WindowProcessListener listener) { mInfo = info; @@ -202,6 +214,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mListener = listener; mAtm = atm; mDisplayId = INVALID_DISPLAY; + + mIsSysUiPackage = info.packageName.equals( + mAtm.getSysUiServiceComponentLocked().getPackageName()); + onConfigurationChanged(atm.getGlobalConfiguration()); } @@ -672,6 +688,23 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return true; } } + if (isEmbedded()) { + return true; + } + } + return false; + } + + /** + * @return {@code true} if this process is rendering content on to a window shown by + * another process. + */ + private boolean isEmbedded() { + for (int i = mHostActivities.size() - 1; i >= 0; --i) { + final ActivityRecord r = mHostActivities.get(i); + if (r.isInterestingToUserLocked()) { + return true; + } } return false; } @@ -814,6 +847,19 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } + /** Adds an activity that hosts UI drawn by the current process. */ + void addHostActivity(ActivityRecord r) { + if (mHostActivities.contains(r)) { + return; + } + mHostActivities.add(r); + } + + /** Removes an activity that hosts UI drawn by the current process. */ + void removeHostActivity(ActivityRecord r) { + mHostActivities.remove(r); + } + public interface ComputeOomAdjCallback { void onVisibleActivity(); void onPausedActivity(); @@ -1039,6 +1085,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio * always track the configuration of the non-finishing activity last added to the process. */ private void updateActivityConfigurationListener() { + if (mIsSysUiPackage || mUid == Process.SYSTEM_UID) { + // This is a system owned process and should not use an activity config. + // TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs. + return; + } + for (int i = mActivities.size() - 1; i >= 0; i--) { final ActivityRecord activityRecord = mActivities.get(i); if (!activityRecord.finishing && !activityRecord.containsListener(this)) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 27f1ca025a93..fc28cd594a92 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -118,6 +118,7 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; +import static com.android.server.wm.WindowContainerChildProto.WINDOW; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; @@ -137,6 +138,7 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT; +import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT; import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; @@ -145,7 +147,6 @@ import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT; import static com.android.server.wm.WindowStateProto.ANIMATOR; import static com.android.server.wm.WindowStateProto.ATTRIBUTES; -import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS; import static com.android.server.wm.WindowStateProto.DESTROYING; import static com.android.server.wm.WindowStateProto.DISPLAY_ID; import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME; @@ -669,6 +670,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET; + static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */ + /** * @return The insets state as requested by the client, i.e. the dispatched insets state * for which the visibilities are overridden with what the client requested. @@ -3758,9 +3761,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mSurfacePosition.dumpDebug(proto, SURFACE_POSITION); mWinAnimator.dumpDebug(proto, ANIMATOR); proto.write(ANIMATING_EXIT, mAnimatingExit); - for (int i = 0; i < mChildren.size(); i++) { - mChildren.get(i).dumpDebug(proto, CHILD_WINDOWS, logLevel); - } proto.write(REQUESTED_WIDTH, mRequestedWidth); proto.write(REQUESTED_HEIGHT, mRequestedHeight); proto.write(VIEW_VISIBILITY, mViewVisibility); @@ -3779,6 +3779,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } @Override + long getProtoFieldId() { + return WINDOW; + } + + @Override public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(HASH_CODE, System.identityHashCode(this)); @@ -5660,8 +5665,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mSession.mPid == pid && isNonToastOrStarting() && isVisibleNow(); } - SurfaceControl getDeferTransactionBarrier() { - return mWinAnimator.getDeferTransactionBarrier(); + SurfaceControl getClientViewRootSurface() { + return mWinAnimator.getClientViewRootSurface(); } @Override @@ -5671,6 +5676,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWaitingListener = waitingListener; mWaitingSyncId = waitingId; mUsingBLASTSyncTransaction = true; + + mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this); + mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this, + BLAST_TIMEOUT_DURATION); + return true; } @@ -5678,6 +5688,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (!mUsingBLASTSyncTransaction) { return mWinAnimator.finishDrawingLocked(postDrawTransaction); } + + mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this); if (postDrawTransaction == null) { postDrawTransaction = new SurfaceControl.Transaction(); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 81d0e3e88c1a..79dc5366445e 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -383,8 +383,9 @@ class WindowStateAnimator { // Make sure to reparent any children of the new surface back to the preserved // surface before destroying it. if (mSurfaceController != null && mPendingDestroySurface != null) { - mPostDrawTransaction.reparentChildren(mSurfaceController.mSurfaceControl, - mPendingDestroySurface.mSurfaceControl).apply(); + mPostDrawTransaction.reparentChildren( + mSurfaceController.getClientViewRootSurface(), + mPendingDestroySurface.mSurfaceControl).apply(); } destroySurfaceLocked(); mSurfaceDestroyDeferred = true; @@ -413,9 +414,9 @@ class WindowStateAnimator { // child layers need to be reparented to the new surface to make this // transparent to the app. if (mWin.mActivityRecord == null || mWin.mActivityRecord.isRelaunching() == false) { - mPostDrawTransaction.reparentChildren(mPendingDestroySurface.mSurfaceControl, - mSurfaceController.mSurfaceControl) - .apply(); + mPostDrawTransaction.reparentChildren( + mPendingDestroySurface.getClientViewRootSurface(), + mSurfaceController.mSurfaceControl).apply(); } } } @@ -875,7 +876,7 @@ class WindowStateAnimator { if (mSurfaceResized && (mAttrType == TYPE_BASE_APPLICATION) && (task != null) && (task.getMainWindowSizeChangeTransaction() != null)) { - mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(), + mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(), mWin.getFrameNumber()); SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction()); task.setMainWindowSizeChangeTransaction(null); @@ -1012,7 +1013,7 @@ class WindowStateAnimator { // the WS position is reset (so the stack position is shown) at the same // time that the buffer size changes. setOffsetPositionForStackResize(false); - mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(), + mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(), mWin.getFrameNumber()); } else { final ActivityStack stack = mWin.getRootTask(); @@ -1043,7 +1044,7 @@ class WindowStateAnimator { // comes in at the new size (normally position and crop are unfrozen). // deferTransactionUntil accomplishes this for us. if (wasForceScaled && !mForceScaleUntilResize) { - mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(), + mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(), mWin.getFrameNumber()); mSurfaceController.forceScaleableInTransaction(false); } @@ -1288,8 +1289,9 @@ class WindowStateAnimator { if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) { final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl; mPostDrawTransaction.reparent(pendingSurfaceControl, null); - mPostDrawTransaction.reparentChildren(pendingSurfaceControl, - mSurfaceController.mSurfaceControl); + mPostDrawTransaction.reparentChildren( + mPendingDestroySurface.getClientViewRootSurface(), + mSurfaceController.mSurfaceControl); } SurfaceControl.mergeToGlobalTransaction(mPostDrawTransaction); @@ -1521,10 +1523,10 @@ class WindowStateAnimator { mOffsetPositionForStackResize = offsetPositionForStackResize; } - SurfaceControl getDeferTransactionBarrier() { + SurfaceControl getClientViewRootSurface() { if (!hasSurface()) { return null; } - return mSurfaceController.getDeferTransactionBarrier(); + return mSurfaceController.getClientViewRootSurface(); } } diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 383c0d9ab3d4..d7c97b922d2d 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -533,7 +533,15 @@ class WindowSurfaceController { return mSurfaceH; } - SurfaceControl getDeferTransactionBarrier() { + /** + * Returns the Surface which the client-framework ViewRootImpl will be using. + * This is either the WSA SurfaceControl or it's BLAST child surface. + * This has too main uses: + * 1. This is the Surface the client will add children to, we use this to make + * sure we don't reparent the BLAST surface itself when calling reparentChildren + * 2. We use this as the barrier Surface for some deferTransaction operations. + */ + SurfaceControl getClientViewRootSurface() { if (mBLASTSurfaceControl != null) { return mBLASTSurfaceControl; } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 98d9c8fcab5a..b7c6af2f7a0d 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -28,13 +28,13 @@ import static com.android.server.wm.ProtoLogGroup.WM_ERROR; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; +import static com.android.server.wm.WindowContainerChildProto.WINDOW_TOKEN; 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.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowTokenProto.HASH_CODE; import static com.android.server.wm.WindowTokenProto.PAUSED; import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW; -import static com.android.server.wm.WindowTokenProto.WINDOWS; import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER; import android.annotation.CallSuper; @@ -50,6 +50,7 @@ import android.view.InsetsState; import android.view.SurfaceControl; import android.view.WindowManager; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.policy.WindowManagerPolicy; import com.android.server.protolog.common.ProtoLog; @@ -99,7 +100,8 @@ class WindowToken extends WindowContainer<WindowState> { private Configuration mLastReportedConfig; private int mLastReportedDisplay = INVALID_DISPLAY; - private final boolean mFromClientToken; + @VisibleForTesting + final boolean mFromClientToken; /** * Used to fix the transform of the token to be rotated to a rotation different than it's @@ -180,13 +182,13 @@ class WindowToken extends WindowContainer<WindowState> { WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) { this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, - false /* roundedCornersOverlay */); + false /* roundedCornerOverlay */); } WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, - DisplayContent dc, boolean ownerCanManageAppTokens, boolean fromClientToken) { + DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, - false /* roundedCornersOverlay */, fromClientToken); + roundedCornerOverlay, false /* fromClientToken */); } WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, @@ -539,15 +541,16 @@ class WindowToken extends WindowContainer<WindowState> { final long token = proto.start(fieldId); super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); proto.write(HASH_CODE, System.identityHashCode(this)); - for (int i = 0; i < mChildren.size(); i++) { - final WindowState w = mChildren.get(i); - w.dumpDebug(proto, WINDOWS, logLevel); - } proto.write(WAITING_TO_SHOW, waitingToShow); proto.write(PAUSED, paused); proto.end(token); } + @Override + long getProtoFieldId() { + return WINDOW_TOKEN; + } + void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); pw.print(prefix); pw.print("windows="); pw.println(mChildren); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c58eae1cf330..312d2d2e2ac2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -148,10 +148,6 @@ import android.app.admin.StartInstallingUpdateCallback; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; -import android.app.timedetector.ManualTimeSuggestion; -import android.app.timedetector.TimeDetector; -import android.app.timezonedetector.ManualTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneDetector; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; import android.compat.annotation.ChangeId; @@ -2118,14 +2114,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mContext.getSystemService(AlarmManager.class); } - TimeDetector getTimeDetector() { - return mContext.getSystemService(TimeDetector.class); - } - - TimeZoneDetector getTimeZoneDetector() { - return mContext.getSystemService(TimeZoneDetector.class); - } - ConnectivityManager getConnectivityManager() { return mContext.getSystemService(ConnectivityManager.class); } @@ -11730,15 +11718,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) { return false; } - ManualTimeSuggestion manualTimeSuggestion = TimeDetector.createManualTimeSuggestion( - millis, "DevicePolicyManagerService: setTime"); - mInjector.binderWithCleanCallingIdentity( - () -> mInjector.getTimeDetector().suggestManualTime(manualTimeSuggestion)); - DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_TIME) .setAdmin(who) .write(); + mInjector.binderWithCleanCallingIdentity(() -> mInjector.getAlarmManager().setTime(millis)); return true; } @@ -11750,11 +11734,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) { return false; } - ManualTimeZoneSuggestion manualTimeZoneSuggestion = - TimeZoneDetector.createManualTimeZoneSuggestion( - timeZone, "DevicePolicyManagerService: setTimeZone"); mInjector.binderWithCleanCallingIdentity(() -> - mInjector.getTimeZoneDetector().suggestManualTimeZone(manualTimeZoneSuggestion)); + mInjector.getAlarmManager().setTimeZone(timeZone)); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_TIME_ZONE) diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 2426e8f29dca..9c9fe8c67de8 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -917,19 +917,23 @@ std::vector<std::string> IncrementalService::listFiles(StorageId storage) const } bool IncrementalService::startLoading(StorageId storage) const { - const auto ifs = getIfs(storage); - if (!ifs) { - return false; - } - std::unique_lock l(ifs->lock); - if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) { - if (ifs->dataLoaderReady.wait_for(l, Seconds(5)) == std::cv_status::timeout) { - LOG(ERROR) << "Timeout waiting for data loader to be ready"; + { + std::unique_lock l(mLock); + const auto& ifs = getIfsLocked(storage); + if (!ifs) { return false; } + if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) { + ifs->dataLoaderStartRequested = true; + return true; + } } + return startDataLoader(storage); +} + +bool IncrementalService::startDataLoader(MountId mountId) const { sp<IDataLoader> dataloader; - auto status = mDataLoaderManager->getDataLoader(ifs->mountId, &dataloader); + auto status = mDataLoaderManager->getDataLoader(mountId, &dataloader); if (!status.isOk()) { return false; } @@ -1065,15 +1069,9 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, } return true; // eventually... } - if (base::GetBoolProperty("incremental.skip_loader", false)) { - LOG(INFO) << "Skipped data loader because of incremental.skip_loader property"; - std::unique_lock l(ifs.lock); - ifs.savedDataLoaderParams.reset(); - return true; - } std::unique_lock l(ifs.lock); - if (ifs.dataLoaderStatus == IDataLoaderStatusListener::DATA_LOADER_CREATED) { + if (ifs.dataLoaderStatus != -1) { LOG(INFO) << "Skipped data loader preparation because it already exists"; return true; } @@ -1226,30 +1224,42 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange externalListener->onStatusChanged(mountId, newStatus); } - std::unique_lock l(incrementalService.mLock); - const auto& ifs = incrementalService.getIfsLocked(mountId); - if (!ifs) { - LOG(WARNING) << "Received data loader status " << int(newStatus) << " for unknown mount " - << mountId; - return binder::Status::ok(); + bool startRequested = false; + { + std::unique_lock l(incrementalService.mLock); + const auto& ifs = incrementalService.getIfsLocked(mountId); + if (!ifs) { + LOG(WARNING) << "Received data loader status " << int(newStatus) << " for unknown mount " + << mountId; + return binder::Status::ok(); + } + ifs->dataLoaderStatus = newStatus; + + if (newStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED) { + ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED; + incrementalService.deleteStorageLocked(*ifs, std::move(l)); + return binder::Status::ok(); + } + + startRequested = ifs->dataLoaderStartRequested; } - ifs->dataLoaderStatus = newStatus; + switch (newStatus) { case IDataLoaderStatusListener::DATA_LOADER_NO_CONNECTION: { // TODO(b/150411019): handle data loader connection loss break; } case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: { - ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STARTED; + // TODO(b/150411019): handle data loader connection loss break; } case IDataLoaderStatusListener::DATA_LOADER_CREATED: { - ifs->dataLoaderReady.notify_one(); + if (startRequested) { + incrementalService.startDataLoader(mountId); + } break; } case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: { - ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED; - incrementalService.deleteStorageLocked(*ifs, std::move(l)); break; } case IDataLoaderStatusListener::DATA_LOADER_STARTED: { diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 8ff441b5a276..406b32e51044 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -170,7 +170,7 @@ private: std::optional<DataLoaderParamsParcel> savedDataLoaderParams; std::atomic<int> nextStorageDirNo{0}; std::atomic<int> dataLoaderStatus = -1; - std::condition_variable dataLoaderReady; + bool dataLoaderStartRequested = false; TimePoint connectionLostTime = TimePoint(); const IncrementalService& incrementalService; @@ -208,6 +208,8 @@ private: bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr, const DataLoaderStatusListener* externalListener = nullptr); + bool startDataLoader(MountId mountId) const; + BindPathMap::const_iterator findStorageLocked(std::string_view path) const; StorageId findStorageId(std::string_view path) const; diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java index 8481e5b916a6..28e3d4b5b744 100644 --- a/services/people/java/com/android/server/people/data/ConversationStore.java +++ b/services/people/java/com/android/server/people/data/ConversationStore.java @@ -89,25 +89,21 @@ class ConversationStore { * Loads conversations from disk to memory in a background thread. This should be called * after the device powers on and the user has been unlocked. */ - @MainThread - void loadConversationsFromDisk() { - mScheduledExecutorService.execute(() -> { - synchronized (this) { - ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = - getConversationInfosProtoDiskReadWriter(); - if (conversationInfosProtoDiskReadWriter == null) { - return; - } - List<ConversationInfo> conversationsOnDisk = - conversationInfosProtoDiskReadWriter.read(CONVERSATIONS_FILE_NAME); - if (conversationsOnDisk == null) { - return; - } - for (ConversationInfo conversationInfo : conversationsOnDisk) { - updateConversationsInMemory(conversationInfo); - } - } - }); + @WorkerThread + synchronized void loadConversationsFromDisk() { + ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = + getConversationInfosProtoDiskReadWriter(); + if (conversationInfosProtoDiskReadWriter == null) { + return; + } + List<ConversationInfo> conversationsOnDisk = + conversationInfosProtoDiskReadWriter.read(CONVERSATIONS_FILE_NAME); + if (conversationsOnDisk == null) { + return; + } + for (ConversationInfo conversationInfo : conversationsOnDisk) { + updateConversationsInMemory(conversationInfo); + } } /** diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 4e0afc525383..7085f96dd31a 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -28,6 +28,7 @@ import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -87,13 +88,13 @@ public class DataManager { private static final String TAG = "DataManager"; - private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS; + private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS; private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L; private final Context mContext; private final Injector mInjector; - private final ScheduledExecutorService mUsageStatsQueryExecutor; - private final ScheduledExecutorService mDiskReadWriterExecutor; + private final ScheduledExecutorService mScheduledExecutor; + private final Object mLock = new Object(); private final SparseArray<UserData> mUserDataArray = new SparseArray<>(); private final SparseArray<BroadcastReceiver> mBroadcastReceivers = new SparseArray<>(); @@ -118,8 +119,7 @@ public class DataManager { DataManager(Context context, Injector injector) { mContext = context; mInjector = injector; - mUsageStatsQueryExecutor = mInjector.createScheduledExecutor(); - mDiskReadWriterExecutor = mInjector.createScheduledExecutor(); + mScheduledExecutor = mInjector.createScheduledExecutor(); } /** Initialization. Called when the system services are up running. */ @@ -138,103 +138,56 @@ public class DataManager { /** This method is called when a user is unlocked. */ public void onUserUnlocked(int userId) { - UserData userData = mUserDataArray.get(userId); - if (userData == null) { - userData = new UserData(userId, mDiskReadWriterExecutor); - mUserDataArray.put(userId, userData); - } - userData.setUserUnlocked(); - updateDefaultDialer(userData); - updateDefaultSmsApp(userData); - - ScheduledFuture<?> scheduledFuture = mUsageStatsQueryExecutor.scheduleAtFixedRate( - new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC, - TimeUnit.SECONDS); - mUsageStatsQueryFutures.put(userId, scheduledFuture); - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED); - intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); - BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId); - mBroadcastReceivers.put(userId, broadcastReceiver); - mContext.registerReceiverAsUser( - broadcastReceiver, UserHandle.of(userId), intentFilter, null, null); - - ContentObserver contactsContentObserver = new ContactsContentObserver( - BackgroundThread.getHandler()); - mContactsContentObservers.put(userId, contactsContentObserver); - mContext.getContentResolver().registerContentObserver( - Contacts.CONTENT_URI, /* notifyForDescendants= */ true, - contactsContentObserver, userId); - - NotificationListener notificationListener = new NotificationListener(); - mNotificationListeners.put(userId, notificationListener); - try { - notificationListener.registerAsSystemService(mContext, - new ComponentName(mContext, getClass()), userId); - } catch (RemoteException e) { - // Should never occur for local calls. - } - - PackageMonitor packageMonitor = new PerUserPackageMonitor(); - packageMonitor.register(mContext, null, UserHandle.of(userId), true); - mPackageMonitors.put(userId, packageMonitor); - - if (userId == UserHandle.USER_SYSTEM) { - // The call log and MMS/SMS messages are shared across user profiles. So only need to - // register the content observers once for the primary user. - // TODO: Register observers after the conversations and events being loaded from disk. - mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler()); - mContext.getContentResolver().registerContentObserver( - CallLog.CONTENT_URI, /* notifyForDescendants= */ true, - mCallLogContentObserver, UserHandle.USER_SYSTEM); - - mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler()); - mContext.getContentResolver().registerContentObserver( - MmsSms.CONTENT_URI, /* notifyForDescendants= */ false, - mMmsSmsContentObserver, UserHandle.USER_SYSTEM); + synchronized (mLock) { + UserData userData = mUserDataArray.get(userId); + if (userData == null) { + userData = new UserData(userId, mScheduledExecutor); + mUserDataArray.put(userId, userData); + } + userData.setUserUnlocked(); } - - DataMaintenanceService.scheduleJob(mContext, userId); + mScheduledExecutor.execute(() -> setupUser(userId)); } /** This method is called when a user is stopping. */ public void onUserStopping(int userId) { - if (mUserDataArray.indexOfKey(userId) >= 0) { - mUserDataArray.get(userId).setUserStopped(); - } - if (mUsageStatsQueryFutures.indexOfKey(userId) >= 0) { - mUsageStatsQueryFutures.get(userId).cancel(true); - } - if (mBroadcastReceivers.indexOfKey(userId) >= 0) { - mContext.unregisterReceiver(mBroadcastReceivers.get(userId)); - } - if (mContactsContentObservers.indexOfKey(userId) >= 0) { - mContext.getContentResolver().unregisterContentObserver( - mContactsContentObservers.get(userId)); - } - if (mNotificationListeners.indexOfKey(userId) >= 0) { - try { - mNotificationListeners.get(userId).unregisterAsSystemService(); - } catch (RemoteException e) { - // Should never occur for local calls. + synchronized (mLock) { + ContentResolver contentResolver = mContext.getContentResolver(); + if (mUserDataArray.indexOfKey(userId) >= 0) { + mUserDataArray.get(userId).setUserStopped(); } - } - if (mPackageMonitors.indexOfKey(userId) >= 0) { - mPackageMonitors.get(userId).unregister(); - } - if (userId == UserHandle.USER_SYSTEM) { - if (mCallLogContentObserver != null) { - mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver); - mCallLogContentObserver = null; + if (mUsageStatsQueryFutures.indexOfKey(userId) >= 0) { + mUsageStatsQueryFutures.get(userId).cancel(true); } - if (mMmsSmsContentObserver != null) { - mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver); - mCallLogContentObserver = null; + if (mBroadcastReceivers.indexOfKey(userId) >= 0) { + mContext.unregisterReceiver(mBroadcastReceivers.get(userId)); + } + if (mContactsContentObservers.indexOfKey(userId) >= 0) { + contentResolver.unregisterContentObserver(mContactsContentObservers.get(userId)); + } + if (mNotificationListeners.indexOfKey(userId) >= 0) { + try { + mNotificationListeners.get(userId).unregisterAsSystemService(); + } catch (RemoteException e) { + // Should never occur for local calls. + } + } + if (mPackageMonitors.indexOfKey(userId) >= 0) { + mPackageMonitors.get(userId).unregister(); + } + if (userId == UserHandle.USER_SYSTEM) { + if (mCallLogContentObserver != null) { + contentResolver.unregisterContentObserver(mCallLogContentObserver); + mCallLogContentObserver = null; + } + if (mMmsSmsContentObserver != null) { + contentResolver.unregisterContentObserver(mMmsSmsContentObserver); + mCallLogContentObserver = null; + } } - } - DataMaintenanceService.cancelJob(mContext, userId); + DataMaintenanceService.cancelJob(mContext, userId); + } } /** @@ -288,6 +241,9 @@ public class DataManager { return; } UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier()); + if (userData == null) { + return; + } PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName()); String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null; @Event.EventType int eventType = mimeTypeToShareEventType(mimeType); @@ -353,6 +309,68 @@ public class DataManager { userData.restore(payload); } + private void setupUser(@UserIdInt int userId) { + synchronized (mLock) { + UserData userData = getUnlockedUserData(userId); + if (userData == null) { + return; + } + userData.loadUserData(); + + updateDefaultDialer(userData); + updateDefaultSmsApp(userData); + + ScheduledFuture<?> scheduledFuture = mScheduledExecutor.scheduleAtFixedRate( + new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC, + TimeUnit.SECONDS); + mUsageStatsQueryFutures.put(userId, scheduledFuture); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED); + intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); + BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId); + mBroadcastReceivers.put(userId, broadcastReceiver); + mContext.registerReceiverAsUser( + broadcastReceiver, UserHandle.of(userId), intentFilter, null, null); + + ContentObserver contactsContentObserver = new ContactsContentObserver( + BackgroundThread.getHandler()); + mContactsContentObservers.put(userId, contactsContentObserver); + mContext.getContentResolver().registerContentObserver( + Contacts.CONTENT_URI, /* notifyForDescendants= */ true, + contactsContentObserver, userId); + + NotificationListener notificationListener = new NotificationListener(); + mNotificationListeners.put(userId, notificationListener); + try { + notificationListener.registerAsSystemService(mContext, + new ComponentName(mContext, getClass()), userId); + } catch (RemoteException e) { + // Should never occur for local calls. + } + + PackageMonitor packageMonitor = new PerUserPackageMonitor(); + packageMonitor.register(mContext, null, UserHandle.of(userId), true); + mPackageMonitors.put(userId, packageMonitor); + + if (userId == UserHandle.USER_SYSTEM) { + // The call log and MMS/SMS messages are shared across user profiles. So only need + // to register the content observers once for the primary user. + mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler()); + mContext.getContentResolver().registerContentObserver( + CallLog.CONTENT_URI, /* notifyForDescendants= */ true, + mCallLogContentObserver, UserHandle.USER_SYSTEM); + + mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler()); + mContext.getContentResolver().registerContentObserver( + MmsSms.CONTENT_URI, /* notifyForDescendants= */ false, + mMmsSmsContentObserver, UserHandle.USER_SYSTEM); + } + + DataMaintenanceService.scheduleJob(mContext, userId); + } + } + private int mimeTypeToShareEventType(String mimeType) { if (mimeType.startsWith("text/")) { return Event.TYPE_SHARE_TEXT; diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java index 00d4241fc5f7..9cf84c94f0f7 100644 --- a/services/people/java/com/android/server/people/data/EventStore.java +++ b/services/people/java/com/android/server/people/data/EventStore.java @@ -17,9 +17,9 @@ package com.android.server.people.data; import android.annotation.IntDef; -import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.WorkerThread; import android.net.Uri; import android.util.ArrayMap; @@ -90,20 +90,16 @@ class EventStore { * Loads existing {@link EventHistoryImpl}s from disk. This should be called when device powers * on and user is unlocked. */ - @MainThread - void loadFromDisk() { - mScheduledExecutorService.execute(() -> { - synchronized (this) { - for (@EventCategory int category = 0; category < mEventsCategoryDirs.size(); - category++) { - File categoryDir = mEventsCategoryDirs.get(category); - Map<String, EventHistoryImpl> existingEventHistoriesImpl = - EventHistoryImpl.eventHistoriesImplFromDisk(categoryDir, - mScheduledExecutorService); - mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl); - } - } - }); + @WorkerThread + synchronized void loadFromDisk() { + for (@EventCategory int category = 0; category < mEventsCategoryDirs.size(); + category++) { + File categoryDir = mEventsCategoryDirs.get(category); + Map<String, EventHistoryImpl> existingEventHistoriesImpl = + EventHistoryImpl.eventHistoriesImplFromDisk(categoryDir, + mScheduledExecutorService); + mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl); + } } /** diff --git a/services/people/java/com/android/server/people/data/MmsQueryHelper.java b/services/people/java/com/android/server/people/data/MmsQueryHelper.java index 1e485c082d18..39dba9c73ba2 100644 --- a/services/people/java/com/android/server/people/data/MmsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/MmsQueryHelper.java @@ -21,6 +21,7 @@ import android.annotation.WorkerThread; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.os.Binder; import android.provider.Telephony.BaseMmsColumns; import android.provider.Telephony.Mms; import android.telephony.PhoneNumberUtils; @@ -71,31 +72,36 @@ class MmsQueryHelper { // NOTE: The field Mms.DATE is stored in seconds, not milliseconds. String[] selectionArgs = new String[] { Long.toString(sinceTime / MILLIS_PER_SECONDS) }; boolean hasResults = false; - try (Cursor cursor = mContext.getContentResolver().query( - Mms.CONTENT_URI, projection, selection, selectionArgs, null)) { - if (cursor == null) { - Slog.w(TAG, "Cursor is null when querying MMS table."); - return false; - } - while (cursor.moveToNext()) { - // ID - int msgIdIndex = cursor.getColumnIndex(Mms._ID); - String msgId = cursor.getString(msgIdIndex); - - // Date - int dateIndex = cursor.getColumnIndex(Mms.DATE); - long date = cursor.getLong(dateIndex) * MILLIS_PER_SECONDS; - - // Message box - int msgBoxIndex = cursor.getColumnIndex(Mms.MESSAGE_BOX); - int msgBox = cursor.getInt(msgBoxIndex); - - mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date); - String address = getMmsAddress(msgId, msgBox); - if (address != null && addEvent(address, date, msgBox)) { - hasResults = true; + Binder.allowBlockingForCurrentThread(); + try { + try (Cursor cursor = mContext.getContentResolver().query( + Mms.CONTENT_URI, projection, selection, selectionArgs, null)) { + if (cursor == null) { + Slog.w(TAG, "Cursor is null when querying MMS table."); + return false; + } + while (cursor.moveToNext()) { + // ID + int msgIdIndex = cursor.getColumnIndex(Mms._ID); + String msgId = cursor.getString(msgIdIndex); + + // Date + int dateIndex = cursor.getColumnIndex(Mms.DATE); + long date = cursor.getLong(dateIndex) * MILLIS_PER_SECONDS; + + // Message box + int msgBoxIndex = cursor.getColumnIndex(Mms.MESSAGE_BOX); + int msgBox = cursor.getInt(msgBoxIndex); + + mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date); + String address = getMmsAddress(msgId, msgBox); + if (address != null && addEvent(address, date, msgBox)) { + hasResults = true; + } } } + } finally { + Binder.defaultBlockingForCurrentThread(); } return hasResults; } diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java index 3e4c992ce70b..28837d5bf148 100644 --- a/services/people/java/com/android/server/people/data/PackageData.java +++ b/services/people/java/com/android/server/people/data/PackageData.java @@ -25,6 +25,7 @@ import static com.android.server.people.data.EventStore.CATEGORY_SMS; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.annotation.WorkerThread; import android.content.LocusId; import android.os.FileUtils; import android.text.TextUtils; @@ -77,6 +78,7 @@ public class PackageData { * Returns a map of package directory names as keys and their associated {@link PackageData}. * This should be called when device is powered on and unlocked. */ + @WorkerThread @NonNull static Map<String, PackageData> packagesDataFromDisk(@UserIdInt int userId, @NonNull Predicate<String> isDefaultDialerPredicate, diff --git a/services/people/java/com/android/server/people/data/SmsQueryHelper.java b/services/people/java/com/android/server/people/data/SmsQueryHelper.java index c38c846bf461..a5eb3a581616 100644 --- a/services/people/java/com/android/server/people/data/SmsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/SmsQueryHelper.java @@ -19,6 +19,7 @@ package com.android.server.people.data; import android.annotation.WorkerThread; import android.content.Context; import android.database.Cursor; +import android.os.Binder; import android.provider.Telephony.Sms; import android.provider.Telephony.TextBasedSmsColumns; import android.telephony.PhoneNumberUtils; @@ -65,35 +66,40 @@ class SmsQueryHelper { String selection = Sms.DATE + " > ?"; String[] selectionArgs = new String[] { Long.toString(sinceTime) }; boolean hasResults = false; - try (Cursor cursor = mContext.getContentResolver().query( - Sms.CONTENT_URI, projection, selection, selectionArgs, null)) { - if (cursor == null) { - Slog.w(TAG, "Cursor is null when querying SMS table."); - return false; - } - while (cursor.moveToNext()) { - // ID - int msgIdIndex = cursor.getColumnIndex(Sms._ID); - String msgId = cursor.getString(msgIdIndex); - - // Date - int dateIndex = cursor.getColumnIndex(Sms.DATE); - long date = cursor.getLong(dateIndex); - - // Type - int typeIndex = cursor.getColumnIndex(Sms.TYPE); - int type = cursor.getInt(typeIndex); - - // Address - int addressIndex = cursor.getColumnIndex(Sms.ADDRESS); - String address = PhoneNumberUtils.formatNumberToE164( - cursor.getString(addressIndex), mCurrentCountryIso); - - mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date); - if (address != null && addEvent(address, date, type)) { - hasResults = true; + Binder.allowBlockingForCurrentThread(); + try { + try (Cursor cursor = mContext.getContentResolver().query( + Sms.CONTENT_URI, projection, selection, selectionArgs, null)) { + if (cursor == null) { + Slog.w(TAG, "Cursor is null when querying SMS table."); + return false; + } + while (cursor.moveToNext()) { + // ID + int msgIdIndex = cursor.getColumnIndex(Sms._ID); + String msgId = cursor.getString(msgIdIndex); + + // Date + int dateIndex = cursor.getColumnIndex(Sms.DATE); + long date = cursor.getLong(dateIndex); + + // Type + int typeIndex = cursor.getColumnIndex(Sms.TYPE); + int type = cursor.getInt(typeIndex); + + // Address + int addressIndex = cursor.getColumnIndex(Sms.ADDRESS); + String address = PhoneNumberUtils.formatNumberToE164( + cursor.getString(addressIndex), mCurrentCountryIso); + + mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date); + if (address != null && addEvent(address, date, type)) { + hasResults = true; + } } } + } finally { + Binder.defaultBlockingForCurrentThread(); } return hasResults; } diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java index ed8c595ab42a..429d5b7193ce 100644 --- a/services/people/java/com/android/server/people/data/UserData.java +++ b/services/people/java/com/android/server/people/data/UserData.java @@ -19,6 +19,7 @@ package com.android.server.people.data; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.annotation.WorkerThread; import android.os.Environment; import android.text.TextUtils; import android.util.ArrayMap; @@ -75,12 +76,6 @@ class UserData { void setUserUnlocked() { mIsUnlocked = true; - - // Ensures per user root directory for people data is present, and attempt to load - // data from disk. - mPerUserPeopleDataDir.mkdirs(); - mPackageDataMap.putAll(PackageData.packagesDataFromDisk(mUserId, this::isDefaultDialer, - this::isDefaultSmsApp, mScheduledExecutorService, mPerUserPeopleDataDir)); } void setUserStopped() { @@ -91,6 +86,15 @@ class UserData { return mIsUnlocked; } + @WorkerThread + void loadUserData() { + mPerUserPeopleDataDir.mkdir(); + Map<String, PackageData> packageDataMap = PackageData.packagesDataFromDisk( + mUserId, this::isDefaultDialer, this::isDefaultSmsApp, mScheduledExecutorService, + mPerUserPeopleDataDir); + mPackageDataMap.putAll(packageDataMap); + } + /** * Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise * creates a new instance and returns it. diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java index 6083ce34a3bd..c45ee7b4d802 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -55,6 +55,7 @@ import android.system.OsConstants; import android.text.TextUtils; import android.util.Pair; +import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerService; @@ -125,6 +126,8 @@ public class ApplicationExitInfoTest { mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal()); mAms.mPackageManagerInt = mPackageManagerInt; + doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent(); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); } @After @@ -847,8 +850,8 @@ public class ApplicationExitInfoTest { app.connectionGroup = connectionGroup; app.setProcState = procState; app.lastMemInfo = spy(new Debug.MemoryInfo()); - doReturn((int) pss).when(app.lastMemInfo).getTotalPss(); - doReturn((int) rss).when(app.lastMemInfo).getTotalRss(); + app.lastPss = pss; + app.mLastRss = rss; return app; } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java index 4722b39cc652..e5ec1f76c554 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -36,6 +36,7 @@ import com.android.server.appop.AppOpsService; import com.android.server.testables.TestableDeviceConfig; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -267,9 +268,7 @@ public final class CachedAppOptimizerTest { @Test public void useFreeze_doesNotListenToDeviceConfigChanges() throws InterruptedException { - if (!mCachedAppOptimizerUnderTest.isFreezerSupported()) { - return; - } + Assume.assumeTrue(mCachedAppOptimizerUnderTest.isFreezerSupported()); assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo( CachedAppOptimizer.DEFAULT_USE_FREEZER); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index fc2ae40daca0..d764a790172c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -75,8 +75,10 @@ import static org.mockito.Mockito.spy; import android.app.IApplicationThread; import android.app.IServiceConnection; +import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManagerInternal; import android.content.pm.ServiceInfo; import android.os.Build; import android.os.IBinder; @@ -87,6 +89,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; +import com.android.server.LocalServices; import com.android.server.wm.ActivityServiceConnectionsHolder; import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowProcessController; @@ -127,6 +130,7 @@ public class MockingOomAdjusterTests { private static final String MOCKAPP5_PROCESSNAME = "test #5"; private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5"; private static Context sContext; + private static PackageManagerInternal sPackageManagerInternal; private static ActivityManagerService sService; @BeforeClass @@ -134,9 +138,15 @@ public class MockingOomAdjusterTests { sContext = getInstrumentation().getTargetContext(); System.setProperty("dexmaker.share_classloader", "true"); + sPackageManagerInternal = mock(PackageManagerInternal.class); + doReturn(new ComponentName("", "")).when(sPackageManagerInternal) + .getSystemUiServiceComponent(); + LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal); + sService = mock(ActivityManagerService.class); sService.mActivityTaskManager = new ActivityTaskManagerService(sContext); sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper()); + sService.mPackageManagerInt = sPackageManagerInternal; sService.mAtmInternal = spy(sService.mActivityTaskManager.getAtmInternal()); sService.mConstants = new ActivityManagerConstants(sContext, sService, diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index eb2dd6461071..2944643b8d12 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -267,6 +267,49 @@ public class CompatConfigTest { } @Test + public void testEnableTargetSdkChangesForPackage() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1L) + .addDisabledChangeWithId(2L) + .addTargetSdkChangeWithId(3, 3L) + .addTargetSdkChangeWithId(4, 4L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("foo.bar") + .withTargetSdk(2) + .build(); + + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + + assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + } + + @Test + public void testDisableTargetSdkChangesForPackage() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1L) + .addDisabledChangeWithId(2L) + .addTargetSdkChangeWithId(3, 3L) + .addTargetSdkChangeWithId(4, 4L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("foo.bar") + .withTargetSdk(2) + .build(); + + assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + + assertThat(compatConfig.disableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + } + + @Test public void testLookupChangeId() throws Exception { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) .addEnabledChangeWithIdAndName(1234L, "MY_CHANGE") diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java index 16291b256c44..425c7247e50f 100644 --- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java @@ -176,7 +176,8 @@ public class OverrideValidatorImplTest { CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) .addTargetSdkChangeWithId(TARGET_SDK, 2) - .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3).build(); + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addDisabledChangeWithId(4).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenReturn(ApplicationInfoBuilder.create() @@ -190,6 +191,8 @@ public class OverrideValidatorImplTest { overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); OverrideAllowedState stateTargetSdkAfterChange = overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); assertThat(stateTargetSdkLessChange) .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_BEFORE)); @@ -197,6 +200,8 @@ public class OverrideValidatorImplTest { .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK)); assertThat(stateTargetSdkAfterChange) .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, -1)); } @Test @@ -343,21 +348,22 @@ public class OverrideValidatorImplTest { } @Test - public void getOverrideAllowedState_finalBuildDisabledChangeDebugApp_rejectOverride() + public void getOverrideAllowedState_finalBuildDisabledChangeDebugApp_allowOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) - .addDisabledChangeWithId(1).build(); + .addDisabledChangeWithId(1).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenReturn(ApplicationInfoBuilder.create() .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK) .debuggable().build()); OverrideAllowedState allowedState = overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); assertThat(allowedState) - .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, -1)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 5ad81b2c4506..c1bcf1fb75a6 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -22,8 +22,6 @@ import android.app.IActivityTaskManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.backup.IBackupManager; -import android.app.timedetector.TimeDetector; -import android.app.timezonedetector.TimeZoneDetector; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.Intent; @@ -236,16 +234,6 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi AlarmManager getAlarmManager() {return services.alarmManager;} @Override - TimeDetector getTimeDetector() { - return services.timeDetector; - } - - @Override - TimeZoneDetector getTimeZoneDetector() { - return services.timeZoneDetector; - } - - @Override LockPatternUtils newLockPatternUtils() { return services.lockPatternUtils; } 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 ac818ea8385f..2a6a24eab3d9 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -67,9 +67,6 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.PasswordMetrics; -import android.app.timedetector.ManualTimeSuggestion; -import android.app.timezonedetector.ManualTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneDetector; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Intent; @@ -3969,19 +3966,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); dpm.setTime(admin1, 0); - - BaseMatcher<ManualTimeSuggestion> hasZeroTime = new BaseMatcher<ManualTimeSuggestion>() { - @Override - public boolean matches(Object item) { - final ManualTimeSuggestion suggestion = (ManualTimeSuggestion) item; - return suggestion.getUtcTime().getValue() == 0; - } - @Override - public void describeTo(Description description) { - description.appendText("ManualTimeSuggestion{utcTime.value=0}"); - } - }; - verify(getServices().timeDetector).suggestManualTime(argThat(hasZeroTime)); + verify(getServices().alarmManager).setTime(0); } public void testSetTimeFailWithPO() throws Exception { @@ -3993,19 +3978,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); dpm.setTime(admin1, 0); - - BaseMatcher<ManualTimeSuggestion> hasZeroTime = new BaseMatcher<ManualTimeSuggestion>() { - @Override - public boolean matches(Object item) { - final ManualTimeSuggestion suggestion = (ManualTimeSuggestion) item; - return suggestion.getUtcTime().getValue() == 0; - } - @Override - public void describeTo(Description description) { - description.appendText("ManualTimeSuggestion{utcTime.value=0}"); - } - }; - verify(getServices().timeDetector).suggestManualTime(argThat(hasZeroTime)); + verify(getServices().alarmManager).setTime(0); } public void testSetTimeWithAutoTimeOn() throws Exception { @@ -4020,9 +3993,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); dpm.setTimeZone(admin1, "Asia/Shanghai"); - ManualTimeZoneSuggestion suggestion = - TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info"); - verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion); + verify(getServices().alarmManager).setTimeZone("Asia/Shanghai"); } public void testSetTimeZoneFailWithPO() throws Exception { @@ -4035,9 +4006,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); dpm.setTimeZone(admin1, "Asia/Shanghai"); - ManualTimeZoneSuggestion suggestion = - TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info"); - verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion); + verify(getServices().alarmManager).setTimeZone("Asia/Shanghai"); } public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 12228c19ca00..8625a1ed9fda 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -207,8 +207,6 @@ public class DpmMockContext extends MockContext { switch (name) { case Context.ALARM_SERVICE: return mMockSystemServices.alarmManager; - case Context.TIME_DETECTOR_SERVICE: - return mMockSystemServices.timeDetector; case Context.USER_SERVICE: return mMockSystemServices.userManager; case Context.POWER_SERVICE: diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 01f1a3e92f2c..bbd4472f7d56 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -33,8 +33,6 @@ import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.app.NotificationManager; import android.app.backup.IBackupManager; -import android.app.timedetector.TimeDetector; -import android.app.timezonedetector.TimeZoneDetector; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -118,8 +116,6 @@ public class MockSystemServices { public final TelephonyManager telephonyManager; public final AccountManager accountManager; public final AlarmManager alarmManager; - public final TimeDetector timeDetector; - public final TimeZoneDetector timeZoneDetector; public final KeyChain.KeyChainConnection keyChainConnection; public final CrossProfileApps crossProfileApps; public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal; @@ -164,8 +160,6 @@ public class MockSystemServices { telephonyManager = mock(TelephonyManager.class); accountManager = mock(AccountManager.class); alarmManager = mock(AlarmManager.class); - timeDetector = mock(TimeDetector.class); - timeZoneDetector = mock(TimeZoneDetector.class); keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS); crossProfileApps = mock(CrossProfileApps.class); persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class); diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java index b0def605db79..ccbaee41af7c 100644 --- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java @@ -18,6 +18,11 @@ package com.android.server.lights; import static android.hardware.lights.LightsRequest.Builder; +import static android.graphics.Color.BLACK; +import static android.graphics.Color.BLUE; +import static android.graphics.Color.GREEN; +import static android.graphics.Color.WHITE; + import static com.google.common.truth.Truth.assertThat; import android.content.Context; @@ -92,7 +97,7 @@ public class LightsServiceTest { // When the session requests to turn 3/4 lights on: LightsManager.LightsSession session = manager.openSession(); - session.setLights(new Builder() + session.requestLights(new Builder() .setLight(manager.getLights().get(0), new LightState(0xf1)) .setLight(manager.getLights().get(1), new LightState(0xf2)) .setLight(manager.getLights().get(2), new LightState(0xf3)) @@ -114,18 +119,18 @@ public class LightsServiceTest { Light micLight = manager.getLights().get(0); // The light should begin by being off. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLACK); // When a session commits changes: LightsManager.LightsSession session = manager.openSession(); - session.setLights(new Builder().setLight(micLight, new LightState(0xff00ff00)).build()); + session.requestLights(new Builder().setLight(micLight, new LightState(GREEN)).build()); // Then the light should turn on. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff00ff00); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(GREEN); // When the session goes away: session.close(); // Then the light should turn off. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLACK); } @Test @@ -138,15 +143,15 @@ public class LightsServiceTest { LightsManager.LightsSession session2 = manager.openSession(); // When session1 and session2 both request the same light: - session1.setLights(new Builder().setLight(micLight, new LightState(0xff0000ff)).build()); - session2.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build()); + session1.requestLights(new Builder().setLight(micLight, new LightState(BLUE)).build()); + session2.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build()); // Then session1 should win because it was created first. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff0000ff); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE); // When session1 goes away: session1.close(); // Then session2 should have its request go into effect. - assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xffffffff); + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE); // When session2 goes away: session2.close(); @@ -162,10 +167,10 @@ public class LightsServiceTest { // When the session turns a light on: LightsManager.LightsSession session = manager.openSession(); - session.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build()); + session.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build()); // And then the session clears it again: - session.setLights(new Builder().clearLight(micLight).build()); + session.requestLights(new Builder().clearLight(micLight).build()); // Then the light should turn back off. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0); diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java index 4e63237a6091..e10cbbfe78ad 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java @@ -273,9 +273,8 @@ public final class ConversationStoreTest { // Ensure that futures were cancelled and the immediate flush occurred. assertEquals(0, mMockScheduledExecutorService.getFutures().size()); - // Expect to see 2 executes: loadConversationFromDisk and saveConversationsToDisk. - // loadConversationFromDisk gets called each time we call #resetConversationStore(). - assertEquals(2, mMockScheduledExecutorService.getExecutes().size()); + // Expect to see 1 execute: saveConversationsToDisk. + assertEquals(1, mMockScheduledExecutorService.getExecutes().size()); resetConversationStore(); ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID); diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index a4d63ac8a564..51996047a74b 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -183,6 +183,11 @@ public final class DataManagerTest { when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any( TimeUnit.class))).thenReturn(mScheduledFuture); + doAnswer(ans -> { + Runnable runnable = (Runnable) ans.getArguments()[0]; + runnable.run(); + return null; + }).when(mExecutorService).execute(any(Runnable.class)); when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY)) .thenReturn(Arrays.asList( 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 049c8e1e5746..6eec649f2ba5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.START_ABORTED; +import static android.app.ActivityManager.START_CANCELED; import static android.app.ActivityManager.START_CLASS_NOT_FOUND; import static android.app.ActivityManager.START_DELIVERED_TO_TOP; import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT; @@ -202,10 +203,11 @@ public class ActivityStarterTests extends ActivityTestsBase { final IApplicationThread caller = mock(IApplicationThread.class); final WindowProcessListener listener = mock(WindowProcessListener.class); + final ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = "com.android.test.package"; final WindowProcessController wpc = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP) - ? null : new WindowProcessController( - service, mock(ApplicationInfo.class), null, 0, -1, null, listener); + ? null : new WindowProcessController(service, ai, null, 0, -1, null, listener); doReturn(wpc).when(service).getProcessController(anyObject()); final Intent intent = new Intent(); @@ -344,6 +346,7 @@ public class ActivityStarterTests extends ActivityTestsBase { doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any()); doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt()); + doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent(); // Never review permissions doReturn(false).when(mMockPackageManager).isPermissionsReviewRequired(any(), anyInt()); @@ -655,6 +658,7 @@ public class ActivityStarterTests extends ActivityTestsBase { final WindowProcessListener listener = mock(WindowProcessListener.class); final ApplicationInfo ai = new ApplicationInfo(); ai.uid = callingUid; + ai.packageName = "com.android.test.package"; final WindowProcessController callerApp = new WindowProcessController(mService, ai, null, callingUid, -1, null, listener); callerApp.setHasForegroundActivities(hasForegroundActivities); @@ -892,7 +896,7 @@ public class ActivityStarterTests extends ActivityTestsBase { .execute(); // Simulate a failed start - starter.postStartActivityProcessing(null, START_ABORTED, null); + starter.postStartActivityProcessing(null, START_CANCELED, null); verify(recentTasks, times(1)).setFreezeTaskListReordering(); verify(recentTasks, times(1)).resetFreezeTaskListReorderingOnTimeout(); @@ -1019,7 +1023,7 @@ public class ActivityStarterTests extends ActivityTestsBase { public void taskAppeared(ActivityManager.RunningTaskInfo info) { } @Override - public void taskVanished(IWindowContainer wc) { + public void taskVanished(ActivityManager.RunningTaskInfo info) { } @Override public void transactionReady(int id, SurfaceControl.Transaction t) { diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index ac4c228fda9a..12d89de8e9ad 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -131,6 +131,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { LocalServices.addService(PackageManagerInternal.class, mMockPmi); when(mMockPmi.getPackageList(any())).thenReturn(new PackageList( Collections.singletonList(TEST_COMPONENT.getPackageName()), /* observer */ null)); + when(mMockPmi.getSystemUiServiceComponent()).thenReturn(new ComponentName("", "")); mTarget.onSystemReady(); final ArgumentCaptor<PackageManagerInternal.PackageListObserver> observerCaptor = diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index c7f94efdfde0..34ac835ae18d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -98,7 +98,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mDisplayContent.mOpeningApps.add(win.mActivityRecord); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter; + new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(); @@ -136,7 +136,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { public void testCancel() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter; + new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(); @@ -150,7 +150,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { public void testTimeout() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter; + new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(); @@ -170,7 +170,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord( - win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter; + win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), + null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(); @@ -201,7 +202,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { public void testNotReallyStarted() { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mController.createRemoteAnimationRecord(win.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null); + new Point(50, 100), null, new Rect(50, 100, 150, 150), null); mController.goodToGo(); verifyNoMoreInteractionsExceptAsBinder(mMockRunner); } @@ -211,9 +212,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1"); final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2"); mController.createRemoteAnimationRecord(win1.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null); + new Point(50, 100), null, new Rect(50, 100, 150, 150), null); final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win2.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter; + new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(); @@ -234,7 +235,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { public void testRemovedBeforeStarted() { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter; + new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); win.mActivityRecord.removeImmediately(); @@ -250,7 +251,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mDisplayContent.mChangingContainers.add(win.mActivityRecord); try { final RemoteAnimationRecord record = mController.createRemoteAnimationRecord( - win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150), + win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), new Rect(0, 0, 200, 200)); assertNotNull(record.mThumbnailAdapter); ((AnimationAdapter) record.mAdapter) @@ -304,7 +305,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mDisplayContent.mOpeningApps.add(win.mActivityRecord); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter; + new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(); @@ -333,7 +334,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mDisplayContent.mOpeningApps.add(win.mActivityRecord); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord, - new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter; + new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); mController.goodToGo(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 55d12dbd0abb..560d03fe7d9e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -40,6 +40,7 @@ import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.IntentFilter; @@ -213,6 +214,9 @@ public class SystemServicesTestRule implements TestRule { anyString(), anyInt()); doReturn(null).when(packageManagerInternal).getDefaultHomeActivity(anyInt()); + ComponentName systemServiceComponent = new ComponentName("android.test.system.service", ""); + doReturn(systemServiceComponent).when(packageManagerInternal).getSystemUiServiceComponent(); + // PowerManagerInternal final PowerManagerInternal pmi = mock(PowerManagerInternal.class); final PowerSaveState state = new PowerSaveState.Builder().build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index 48a583cad7a3..53a3682a1efa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -129,7 +129,7 @@ public class TaskOrganizerTests extends WindowTestsBase { final Task task = createTaskInStack(stack, 0 /* userId */); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED); - + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); verify(organizer).taskAppeared(any()); stack.setWindowingMode(WINDOWING_MODE_PINNED); @@ -345,7 +345,7 @@ public class TaskOrganizerTests extends WindowTestsBase { public void taskAppeared(RunningTaskInfo taskInfo) { } @Override - public void taskVanished(IWindowContainer container) { } + public void taskVanished(RunningTaskInfo container) { } @Override public void transactionReady(int id, SurfaceControl.Transaction t) { } @@ -399,7 +399,7 @@ public class TaskOrganizerTests extends WindowTestsBase { public void taskAppeared(RunningTaskInfo taskInfo) { } @Override - public void taskVanished(IWindowContainer container) { } + public void taskVanished(RunningTaskInfo container) { } @Override public void transactionReady(int id, SurfaceControl.Transaction t) { } @@ -539,7 +539,7 @@ public class TaskOrganizerTests extends WindowTestsBase { mInfo = info; } @Override - public void taskVanished(IWindowContainer wc) { + public void taskVanished(RunningTaskInfo info) { } @Override public void transactionReady(int id, SurfaceControl.Transaction t) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 35723abb4310..da4bde59a09e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -16,9 +16,16 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.TYPE_TOAST; + import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + import android.content.pm.PackageManager; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -57,4 +64,25 @@ public class WindowManagerServiceTests extends WindowTestsBase { return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE); } + + @Test + public void testAddWindowToken() { + IBinder token = mock(IBinder.class); + mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId()); + + WindowToken windowToken = mWm.mRoot.getWindowToken(token); + assertFalse(windowToken.mRoundedCornerOverlay); + assertFalse(windowToken.mFromClientToken); + } + + @Test + public void testAddWindowTokenWithOptions() { + IBinder token = mock(IBinder.class); + mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(), + null /* options */, null /* options */); + + WindowToken windowToken = mWm.mRoot.getWindowToken(token); + assertFalse(windowToken.mRoundedCornerOverlay); + assertTrue(windowToken.mFromClientToken); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 34e487bc1e94..07a6179c00bd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.app.IApplicationThread; +import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.platform.test.annotations.Presubmit; @@ -55,8 +56,11 @@ public class WindowProcessControllerTests extends ActivityTestsBase { @Before public void setUp() { mMockListener = mock(WindowProcessListener.class); + + ApplicationInfo info = mock(ApplicationInfo.class); + info.packageName = "test.package.name"; mWpc = new WindowProcessController( - mService, mock(ApplicationInfo.class), null, 0, -1, null, mMockListener); + mService, info, null, 0, -1, null, mMockListener); mWpc.setThread(mock(IApplicationThread.class)); } @@ -176,6 +180,26 @@ public class WindowProcessControllerTests extends ActivityTestsBase { assertEquals(mWpc.getLastReportedConfiguration(), newConfig); } + @Test + public void testActivityNotOverridingSystemUiProcessConfig() { + final ComponentName systemUiServiceComponent = mService.getSysUiServiceComponentLocked(); + ApplicationInfo applicationInfo = mock(ApplicationInfo.class); + applicationInfo.packageName = systemUiServiceComponent.getPackageName(); + + WindowProcessController wpc = new WindowProcessController( + mService, applicationInfo, null, 0, -1, null, mMockListener); + wpc.setThread(mock(IApplicationThread.class)); + + final ActivityRecord activity = new ActivityBuilder(mService) + .setCreateTask(true) + .setUseProcess(wpc) + .build(); + + wpc.addActivityIfNeeded(activity); + // System UI owned processes should not be registered for activity config changes. + assertFalse(wpc.registeredForActivityConfigChanges()); + } + private TestDisplayContent createTestDisplayContentInContainer() { return new TestDisplayContent.Builder(mService, 1000, 1500).build(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index e6aed498cf1c..38f643daec27 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -25,7 +25,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -129,4 +131,28 @@ public class WindowTokenTests extends WindowTestsBase { // Verify that the token windows are no longer attached to it. assertEquals(0, token.getWindowsCount()); } + + /** + * Test that {@link WindowToken} constructor parameters is set with expectation. + */ + @Test + public void testWindowTokenConstructorSanity() { + WindowToken token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), + TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */); + assertFalse(token.mRoundedCornerOverlay); + assertFalse(token.mFromClientToken); + + token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, + true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */, + true /* roundedCornerOverlay */); + assertTrue(token.mRoundedCornerOverlay); + assertFalse(token.mFromClientToken); + + token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, + true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */, + true /* roundedCornerOverlay */, true /* fromClientToken */); + assertTrue(token.mRoundedCornerOverlay); + assertTrue(token.mFromClientToken); + } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java index af81ab6339f3..be0987d745ba 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java @@ -257,6 +257,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { int userHandle) { SQLiteDatabase db = getReadableDatabase(); Cursor c = db.rawQuery(selectQuery, null); + if (DBG) { + Slog.w(TAG, "querying database: " + selectQuery); + } try { if (c.moveToFirst()) { @@ -334,7 +337,10 @@ public class DatabaseHelper extends SQLiteOpenHelper { return model; } while (c.moveToNext()); } - Slog.w(TAG, "No SoundModel available for the given keyphrase"); + + if (DBG) { + Slog.w(TAG, "No SoundModel available for the given keyphrase"); + } } finally { c.close(); db.close(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 0b24dd2fe15a..0eba07b118b5 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -41,6 +41,7 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.soundtrigger.IRecognitionStatusCallback; +import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.hardware.soundtrigger.KeyphraseMetadata; import android.hardware.soundtrigger.ModelParams; import android.hardware.soundtrigger.SoundTrigger; @@ -223,6 +224,7 @@ public class VoiceInteractionManagerService extends SystemService { class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub { VoiceInteractionManagerServiceImpl mImpl; + KeyphraseEnrollmentInfo mEnrollmentApplicationInfo; private boolean mSafeMode; private int mCurUser; @@ -447,6 +449,15 @@ public class VoiceInteractionManagerService extends SystemService { } } + private void getOrCreateEnrollmentApplicationInfo() { + synchronized (this) { + if (mEnrollmentApplicationInfo == null) { + mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo( + mContext.getPackageManager()); + } + } + } + private void setCurrentUserLocked(@UserIdInt int userHandle) { mCurUser = userHandle; final UserInfo userInfo = mUserManagerInternal.getUserInfo(mCurUser); @@ -1380,12 +1391,17 @@ public class VoiceInteractionManagerService extends SystemService { pw.println(" mCurUserUnlocked: " + mCurUserUnlocked); pw.println(" mCurUserSupported: " + mCurUserSupported); dumpSupportedUsers(pw, " "); + if (mEnrollmentApplicationInfo == null) { + pw.println(" (No enrollment application info)"); + } else { + pw.println(" " + mEnrollmentApplicationInfo.toString()); + } mDbHelper.dump(pw); if (mImpl == null) { pw.println(" (No active implementation)"); - return; + } else { + mImpl.dumpLocked(fd, pw, args); } - mImpl.dumpLocked(fd, pw, args); } mSoundTriggerInternal.dump(fd, pw, args); } @@ -1438,8 +1454,9 @@ public class VoiceInteractionManagerService extends SystemService { } private boolean isCallerTrustedEnrollmentApplication() { - return mImpl.mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication( - Binder.getCallingUid()); + getOrCreateEnrollmentApplicationInfo(); + return mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication( + Binder.getCallingUid()); } private void setImplLocked(VoiceInteractionManagerServiceImpl impl) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index b813f87f335f..a62b03ca82e4 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -36,7 +36,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; -import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -79,7 +78,6 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne final IActivityManager mAm; final IActivityTaskManager mAtm; final VoiceInteractionServiceInfo mInfo; - final KeyphraseEnrollmentInfo mEnrollmentApplicationInfo; final ComponentName mSessionComponentName; final IWindowManager mIWindowManager; boolean mBound = false; @@ -135,7 +133,6 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne mComponent = service; mAm = ActivityManager.getService(); mAtm = ActivityTaskManager.getService(); - mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo(context.getPackageManager()); VoiceInteractionServiceInfo info; try { info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser); @@ -406,7 +403,6 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne pw.println(" Active session:"); mActiveSession.dump(" ", pw); } - pw.println(" " + mEnrollmentApplicationInfo.toString()); } void startLocked() { diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index 9ae86c8c586b..dcd4eb519b74 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -1,7 +1,6 @@ package android.telephony; import android.annotation.IntDef; -import android.provider.Telephony; import android.telecom.Connection; import android.telephony.data.ApnSetting; @@ -653,15 +652,6 @@ public class Annotation { @Retention(RetentionPolicy.SOURCE) public @interface UiccAppType{} - /** @hide */ - @IntDef({ - Telephony.Carriers.SKIP_464XLAT_DEFAULT, - Telephony.Carriers.SKIP_464XLAT_DISABLE, - Telephony.Carriers.SKIP_464XLAT_ENABLE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Skip464XlatStatus {} - /** * Override network type */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c5c08c2ee668..c20748bcb693 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1568,6 +1568,7 @@ public class CarrierConfigManager { /** * The string is used to compare with operator name. * If it matches the pattern then show specific data icon. + * @hide */ public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = "show_carrier_data_icon_pattern_string"; @@ -2978,9 +2979,9 @@ public class CarrierConfigManager { * UE wants to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise not * define. * The configuration is: "connected_mmwave:5G_Plus,connected:5G" + * @hide */ - public static final String KEY_5G_ICON_CONFIGURATION_STRING = - "5g_icon_configuration_string"; + public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string"; /** * Timeout in seconds for displaying 5G icon, default value is 0 which means the timer is @@ -2992,12 +2993,14 @@ public class CarrierConfigManager { * * If 5G is reacquired during this timer, the timer is canceled and restarted when 5G is next * lost. Allows us to momentarily lose 5G without blinking the icon. + * @hide */ public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT = "5g_icon_display_grace_period_sec_int"; /** * Controls time in milliseconds until DcTracker reevaluates 5G connection state. + * @hide */ public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long"; @@ -3527,6 +3530,15 @@ public class CarrierConfigManager { "support_wps_over_ims_bool"; /** + * The two digital number pattern of MMI code which is defined by carrier. + * If the dial number matches this pattern, it will be dialed out normally not USSD. + * + * @hide + */ + public static final String KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY = + "mmi_two_digit_number_pattern_string_array"; + + /** * Holds the list of carrier certificate hashes. * Note that each carrier has its own certificates. */ @@ -4021,7 +4033,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false); sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */); sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING, - "connected_mmwave:5G,connected:5G"); + "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G," + + "not_restricted_rrc_con:5G"); sDefaults.putInt(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, 0); /* Default value is 1 hour. */ sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000); @@ -4082,6 +4095,7 @@ public class CarrierConfigManager { new int[] {4 /* BUSY */}); sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000); + sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]); sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT, CellSignalStrengthLte.USE_RSRP); // Default wifi configurations. diff --git a/telephony/java/android/telephony/CdmaEriInformation.java b/telephony/java/android/telephony/CdmaEriInformation.java index 1cd9d3048526..fd0b905e9c3e 100644 --- a/telephony/java/android/telephony/CdmaEriInformation.java +++ b/telephony/java/android/telephony/CdmaEriInformation.java @@ -40,7 +40,6 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ -@SystemApi public final class CdmaEriInformation implements Parcelable { /** @hide */ @Retention(RetentionPolicy.SOURCE) diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java index 270eafe642b7..c667165e7a0e 100644 --- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java +++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java @@ -208,7 +208,6 @@ public final class DataSpecificRegistrationInfo implements Parcelable { * @return {@code true} if using carrier aggregation. * @hide */ - @SystemApi public boolean isUsingCarrierAggregation() { return mIsUsingCarrierAggregation; } diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index fe75266011ed..34bac5de4c43 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -34,8 +34,9 @@ public class ImsManager { private Context mContext; /** - * <p>Broadcast Action: Indicates that an IMS operation was rejected by the network due to it - * not being authorized on the network. + * <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the + * network due to the network returning a "forbidden" response. This may be due to a + * provisioning change from the network. * May include the {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX} extra to also specify * which subscription the operation was rejected for. * <p class="note"> diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index c74e17f6bca1..1a79bf7d0c22 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -367,6 +367,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * Get the 5G NR connection state. * * @return the 5G NR connection state. + * @hide */ public @NRState int getNrState() { return mNrState; diff --git a/telephony/java/android/telephony/PinResult.java b/telephony/java/android/telephony/PinResult.java index c14bd91d022c..98d6448e77ea 100644 --- a/telephony/java/android/telephony/PinResult.java +++ b/telephony/java/android/telephony/PinResult.java @@ -19,7 +19,6 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,7 +31,6 @@ import java.util.Objects; * * @hide */ -@SystemApi public final class PinResult implements Parcelable { /** @hide */ @IntDef({ diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 9b1baef1a70a..45cba518f933 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -689,8 +689,9 @@ public class ServiceState implements Parcelable { * @return true if registration indicates roaming, false otherwise * @hide */ - @SystemApi public boolean getDataRoamingFromRegistration() { + // TODO: all callers should refactor to get roaming state directly from modem + // this should not be exposed as a public API return mIsDataRoamingFromRegistration; } @@ -1422,7 +1423,6 @@ public class ServiceState implements Parcelable { * @return the frequency range of 5G NR. * @hide */ - @SystemApi public @FrequencyRange int getNrFrequencyRange() { return mNrFrequencyRange; } @@ -2026,6 +2026,7 @@ public class ServiceState implements Parcelable { * The long format can be up to 16 characters long. * * @return long raw name of operator, null if unregistered or unknown + * @hide */ @Nullable public String getOperatorAlphaLongRaw() { @@ -2045,6 +2046,7 @@ public class ServiceState implements Parcelable { * The short format can be up to 8 characters long. * * @return short raw name of operator, null if unregistered or unknown + * @hide */ @Nullable public String getOperatorAlphaShortRaw() { diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 8ac9023b33dc..5a840de36cfb 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1300,8 +1300,13 @@ public class SubscriptionManager { * both active and hidden SubscriptionInfos. * */ - public @Nullable List<SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList() { - return getActiveSubscriptionInfoList(/* userVisibleonly */false); + public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() { + List<SubscriptionInfo> completeList = getActiveSubscriptionInfoList( + /* userVisibleonly */false); + if (completeList == null) { + completeList = new ArrayList<>(); + } + return completeList; } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 40def40c67a9..c705258de4c7 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -54,6 +54,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; @@ -5605,7 +5606,6 @@ public class TelephonyManager { * @hide */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @SystemApi @NonNull public CdmaEriInformation getCdmaEriInformation() { return new CdmaEriInformation( @@ -8725,7 +8725,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public PinResult supplyPinReportPinResult(@NonNull String pin) { @@ -8750,7 +8749,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public PinResult supplyPukReportPinResult(@NonNull String puk, @NonNull String pin) { @@ -11648,11 +11646,9 @@ public class TelephonyManager { } /** - * Override the file path for testing OTA emergency number database in a file partition. + * Override the file path for OTA emergency number database in a file partition. * - * @param otaFilePath The test OTA emergency number database file path; - * if "RESET", recover the original database file partition. - * Format: <root file folder>@<file path> + * @param otaParcelFileDescriptor parcelable file descriptor for OTA emergency number database. * * <p> Requires permission: * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} @@ -11662,16 +11658,42 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) @SystemApi @TestApi - public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String otaFilePath) { + public void updateOtaEmergencyNumberDbFilePath( + @NonNull ParcelFileDescriptor otaParcelFileDescriptor) { try { ITelephony telephony = getITelephony(); if (telephony != null) { - telephony.updateTestOtaEmergencyNumberDbFilePath(otaFilePath); + telephony.updateOtaEmergencyNumberDbFilePath(otaParcelFileDescriptor); } else { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - Log.e(TAG, "notifyOtaEmergencyNumberDatabaseInstalled RemoteException", ex); + Log.e(TAG, "updateOtaEmergencyNumberDbFilePath RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + } + + /** + * Reset the file path to default for OTA emergency number database in a file partition. + * + * <p> Requires permission: + * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + @SystemApi + @TestApi + public void resetOtaEmergencyNumberDbFilePath() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.resetOtaEmergencyNumberDbFilePath(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "resetOtaEmergencyNumberDbFilePath RemoteException", ex); ex.rethrowAsRuntimeException(); } } @@ -12211,12 +12233,14 @@ public class TelephonyManager { /** * It indicates whether modem is enabled or not per slot. - * It's the corresponding status of {@link #enableModemForSlot}. + * It's the corresponding status of TelephonyManager.enableModemForSlot. * + * <p>Requires Permission: + * READ_PRIVILEGED_PHONE_STATE or + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * @param slotIndex which slot it's checking. - * @hide */ - @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int slotIndex) { try { ITelephony telephony = getITelephony(); diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index f5dfacc6a0be..bfb54b008cd8 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -18,7 +18,6 @@ package android.telephony.data; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.content.ContentValues; import android.database.Cursor; import android.hardware.radio.V1_5.ApnTypes; @@ -27,7 +26,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; import android.provider.Telephony.Carriers; -import android.telephony.Annotation; import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetworkType; import android.telephony.ServiceState; @@ -126,6 +124,15 @@ public class ApnSetting implements Parcelable { /** Authentication type for PAP or CHAP. */ public static final int AUTH_TYPE_PAP_OR_CHAP = 3; + /** @hide */ + @IntDef({ + Telephony.Carriers.SKIP_464XLAT_DEFAULT, + Telephony.Carriers.SKIP_464XLAT_DISABLE, + Telephony.Carriers.SKIP_464XLAT_ENABLE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Skip464XlatStatus {} + /** * APN types for data connections. These are usage categories for an APN * entry. One APN entry may support multiple APN types, eg, a single APN @@ -139,7 +146,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_ALL_STRING = "*"; /** @@ -147,7 +153,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_DEFAULT_STRING = "default"; @@ -156,7 +161,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_MMS_STRING = "mms"; @@ -165,7 +169,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_SUPL_STRING = "supl"; /** @@ -173,7 +176,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_DUN_STRING = "dun"; /** @@ -181,7 +183,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_HIPRI_STRING = "hipri"; /** @@ -189,7 +190,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_FOTA_STRING = "fota"; /** @@ -197,7 +197,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_IMS_STRING = "ims"; /** @@ -205,7 +204,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_CBS_STRING = "cbs"; /** @@ -213,7 +211,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_IA_STRING = "ia"; /** @@ -222,7 +219,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_EMERGENCY_STRING = "emergency"; /** @@ -230,7 +226,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_MCX_STRING = "mcx"; /** @@ -238,7 +233,6 @@ public class ApnSetting implements Parcelable { * * @hide */ - @SystemApi public static final String TYPE_XCAP_STRING = "xcap"; @@ -745,7 +739,7 @@ public class ApnSetting implements Parcelable { * @return SKIP_464XLAT_DEFAULT, SKIP_464XLAT_DISABLE or SKIP_464XLAT_ENABLE * @hide */ - @Annotation.Skip464XlatStatus + @Skip464XlatStatus public int getSkip464Xlat() { return mSkip464Xlat; } @@ -1416,7 +1410,6 @@ public class ApnSetting implements Parcelable { * @return comma delimited list of APN types. * @hide */ - @SystemApi @NonNull public static String getApnTypesStringFromBitmask(int apnTypeBitmask) { List<String> types = new ArrayList<>(); @@ -2065,7 +2058,7 @@ public class ApnSetting implements Parcelable { * @param skip464xlat skip464xlat for this APN. * @hide */ - public Builder setSkip464Xlat(@Annotation.Skip464XlatStatus int skip464xlat) { + public Builder setSkip464Xlat(@Skip464XlatStatus int skip464xlat) { this.mSkip464Xlat = skip464xlat; return this; } diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 9c1be48e247a..1597cd5a2f89 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -18,7 +18,6 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; @@ -718,11 +717,16 @@ public final class ImsCallProfile implements Parcelable { * @return A {@link Bundle} containing proprietary call extras that were not set by the * platform. */ - public @Nullable Bundle getProprietaryCallExtras() { + public @NonNull Bundle getProprietaryCallExtras() { if (mCallExtras == null) { - return null; + return new Bundle(); + } + Bundle proprietaryExtras = mCallExtras.getBundle(EXTRA_OEM_EXTRAS); + if (proprietaryExtras == null) { + return new Bundle(); } - return mCallExtras.getBundle(EXTRA_OEM_EXTRAS); + // Make a copy so users do not accidentally change this copy of the extras. + return new Bundle(proprietaryExtras); } public ImsStreamMediaProfile getMediaProfile() { diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java index 025721c89f70..81af99fb40b7 100644 --- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java +++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java @@ -58,7 +58,7 @@ public class ImsCallSessionListener { try { mListener.callSessionProgressing(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -71,7 +71,7 @@ public class ImsCallSessionListener { try { mListener.callSessionInitiated(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -85,7 +85,7 @@ public class ImsCallSessionListener { try { mListener.callSessionInitiatedFailed(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -98,7 +98,7 @@ public class ImsCallSessionListener { try { mListener.callSessionTerminated(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -115,7 +115,7 @@ public class ImsCallSessionListener { try { mListener.callSessionHeld(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -128,7 +128,7 @@ public class ImsCallSessionListener { try { mListener.callSessionHoldFailed(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -141,7 +141,7 @@ public class ImsCallSessionListener { try { mListener.callSessionHoldReceived(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -155,7 +155,7 @@ public class ImsCallSessionListener { try { mListener.callSessionResumed(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -169,7 +169,7 @@ public class ImsCallSessionListener { try { mListener.callSessionResumeFailed(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -182,7 +182,7 @@ public class ImsCallSessionListener { try { mListener.callSessionResumeReceived(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -201,7 +201,7 @@ public class ImsCallSessionListener { mListener.callSessionMergeStarted(newSession != null ? newSession.getServiceImpl() : null, profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -216,7 +216,7 @@ public class ImsCallSessionListener { try { mListener.callSessionMergeStarted(newSession, profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -232,7 +232,7 @@ public class ImsCallSessionListener { mListener.callSessionMergeComplete(newSession != null ? newSession.getServiceImpl() : null); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -247,7 +247,7 @@ public class ImsCallSessionListener { try { mListener.callSessionMergeComplete(newSession); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -260,7 +260,7 @@ public class ImsCallSessionListener { try { mListener.callSessionMergeFailed(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -273,7 +273,7 @@ public class ImsCallSessionListener { try { mListener.callSessionUpdated(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -286,7 +286,7 @@ public class ImsCallSessionListener { try { mListener.callSessionUpdateFailed(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -299,7 +299,7 @@ public class ImsCallSessionListener { try { mListener.callSessionUpdateReceived(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -319,7 +319,7 @@ public class ImsCallSessionListener { mListener.callSessionConferenceExtended( newSession != null ? newSession.getServiceImpl() : null, profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -333,7 +333,7 @@ public class ImsCallSessionListener { try { mListener.callSessionConferenceExtended(newSession, profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -347,7 +347,7 @@ public class ImsCallSessionListener { try { mListener.callSessionConferenceExtendFailed(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -364,7 +364,7 @@ public class ImsCallSessionListener { mListener.callSessionConferenceExtendReceived(newSession != null ? newSession.getServiceImpl() : null, profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -379,7 +379,7 @@ public class ImsCallSessionListener { try { mListener.callSessionConferenceExtendReceived(newSession, profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -391,7 +391,7 @@ public class ImsCallSessionListener { try { mListener.callSessionInviteParticipantsRequestDelivered(); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -407,7 +407,7 @@ public class ImsCallSessionListener { try { mListener.callSessionInviteParticipantsRequestFailed(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -419,7 +419,7 @@ public class ImsCallSessionListener { try { mListener.callSessionRemoveParticipantsRequestDelivered(); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -435,7 +435,7 @@ public class ImsCallSessionListener { try { mListener.callSessionInviteParticipantsRequestFailed(reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -448,7 +448,7 @@ public class ImsCallSessionListener { try { mListener.callSessionConferenceStateUpdated(state); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -465,7 +465,7 @@ public class ImsCallSessionListener { try { mListener.callSessionUssdMessageReceived(mode, ussdMessage); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -501,7 +501,7 @@ public class ImsCallSessionListener { try { mListener.callSessionMayHandover(srcNetworkType, targetNetworkType); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -537,7 +537,7 @@ public class ImsCallSessionListener { try { mListener.callSessionHandover(srcNetworkType, targetNetworkType, reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -570,7 +570,7 @@ public class ImsCallSessionListener { try { mListener.callSessionHandoverFailed(srcNetworkType, targetNetworkType, reasonInfo); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -587,7 +587,7 @@ public class ImsCallSessionListener { try { mListener.callSessionTtyModeReceived(mode); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -600,7 +600,7 @@ public class ImsCallSessionListener { try { mListener.callSessionMultipartyStateChanged(isMultiParty); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -614,7 +614,7 @@ public class ImsCallSessionListener { try { mListener.callSessionSuppServiceReceived(suppSrvNotification); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -628,7 +628,7 @@ public class ImsCallSessionListener { try { mListener.callSessionRttModifyRequestReceived(callProfile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -641,7 +641,7 @@ public class ImsCallSessionListener { try { mListener.callSessionRttModifyResponseReceived(status); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -654,7 +654,7 @@ public class ImsCallSessionListener { try { mListener.callSessionRttMessageReceived(rttMessage); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -667,7 +667,7 @@ public class ImsCallSessionListener { try { mListener.callSessionRttAudioIndicatorChanged(profile); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } @@ -680,7 +680,7 @@ public class ImsCallSessionListener { try { mListener.callQualityChanged(callQuality); } catch (RemoteException e) { - throw new RuntimeException(e); + e.rethrowFromSystemServer(); } } } diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java index bc12404461c7..460a032ce7e0 100644 --- a/telephony/java/android/telephony/ims/ImsUtListener.java +++ b/telephony/java/android/telephony/ims/ImsUtListener.java @@ -49,7 +49,8 @@ public class ImsUtListener { * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_RESTRICTED}, and * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_ALLOWED}. * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)} - * instead. + * instead, this key has been added for backwards compatibility with older proprietary + * implementations only and is being phased out. */ @Deprecated public static final String BUNDLE_KEY_CLIR = "queryClir"; @@ -60,7 +61,8 @@ public class ImsUtListener { * response. The value will be an instance of {@link ImsSsInfo}, which contains the response to * the query. * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)} - * instead. + * instead, this key has been added for backwards compatibility with older proprietary + * implementations only and is being phased out. */ @Deprecated public static final String BUNDLE_KEY_SSINFO = "imsSsInfo"; @@ -123,7 +125,7 @@ public class ImsUtListener { try { mServiceInterface.lineIdentificationSupplementaryServiceResponse(id, configuration); } catch (RemoteException e) { - Log.w(LOG_TAG, "onLineIdentificationSupplementaryServicesResponse: remote exception"); + e.rethrowFromSystemServer(); } } diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 037084608d26..00fa94201297 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -305,13 +305,13 @@ public class ProvisioningManager { /** * An integer key associated with the carrier configured expiration time in seconds for - * RCS presence published offline availability in RCS presence. + * published offline availability in RCS presence provided, which is provided to the network. * <p> * Value is in Integer format. * @see #setProvisioningIntValue(int, int) * @see #getProvisioningIntValue(int) */ - public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; + public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; /** * An integer key associated with whether or not capability discovery is provisioned for this @@ -326,8 +326,10 @@ public class ProvisioningManager { public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; /** - * An integer key associated with the period of time the capability information of each contact - * is cached on the device. + * An integer key associated with the period of time in seconds the capability information of + * each contact is cached on the device. + * <p> + * Seconds are used because this is usually measured in the span of days. * <p> * Value is in Integer format. * @see #setProvisioningIntValue(int, int) @@ -337,7 +339,8 @@ public class ProvisioningManager { /** * An integer key associated with the period of time in seconds that the availability - * information of a contact is cached on the device. + * information of a contact is cached on the device, which is based on the carrier provisioning + * configuration from the network. * <p> * Value is in Integer format. * @see #setProvisioningIntValue(int, int) @@ -347,7 +350,8 @@ public class ProvisioningManager { /** * An integer key associated with the carrier configured interval in seconds expected between - * successive capability polling attempts. + * successive capability polling attempts, which is based on the carrier provisioning + * configuration from the network. * <p> * Value is in Integer format. * @see #setProvisioningIntValue(int, int) @@ -357,7 +361,7 @@ public class ProvisioningManager { /** * An integer key representing the minimum time allowed between two consecutive presence publish - * messages from the device. + * messages from the device in milliseconds. * <p> * Value is in Integer format. * @see #setProvisioningIntValue(int, int) @@ -378,7 +382,7 @@ public class ProvisioningManager { /** * An integer associated with the expiration timer used during the SIP subscription of a * Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact - * book. + * book. This timer value is sent in seconds to the network. * <p> * Value is in Integer format. * @see #setProvisioningIntValue(int, int) @@ -470,7 +474,8 @@ public class ProvisioningManager { public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; /** - * Registration retry Base Time value in seconds. + * Registration retry Base Time value in seconds, which is based off of the carrier + * configuration. * Value is in Integer format. * @see #setProvisioningIntValue(int, int) * @see #getProvisioningIntValue(int) @@ -478,7 +483,8 @@ public class ProvisioningManager { public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; /** - * Registration retry Max Time value in seconds. + * Registration retry Max Time value in seconds, which is based off of the carrier + * configuration. * Value is in Integer format. * @see #setProvisioningIntValue(int, int) * @see #getProvisioningIntValue(int) diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 58e9b7008050..30306c7f9dea 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -24,12 +24,10 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Context; -import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.provider.Telephony; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.IRcsUceControllerCallback; @@ -138,7 +136,7 @@ public class RcsUceAdapter { * UCE. * @hide */ - public static final int PUBLISH_STATE_200_OK = 1; + public static final int PUBLISH_STATE_OK = 1; /** * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. @@ -178,7 +176,7 @@ public class RcsUceAdapter { /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "PUBLISH_STATE_", value = { - PUBLISH_STATE_200_OK, + PUBLISH_STATE_OK, PUBLISH_STATE_NOT_PUBLISHED, PUBLISH_STATE_VOLTE_PROVISION_ERROR, PUBLISH_STATE_RCS_PROVISION_ERROR, @@ -305,7 +303,7 @@ public class RcsUceAdapter { * Gets the last publish result from the UCE service if the device is using an RCS presence * server. * @return The last publish result from the UCE service. If the device is using SIP OPTIONS, - * this method will return {@link #PUBLISH_STATE_200_OK} as well. + * this method will return {@link #PUBLISH_STATE_OK} as well. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index 96f77d809183..d0cec52dfc86 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -270,11 +270,12 @@ public class ImsConfig { /** * Requested expiration for Published Offline availability. * Value is in Integer format. - * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC}. + * @deprecated use + * {@link ProvisioningManager#KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC}. */ @Deprecated public static final int PUBLISH_TIMER_EXTENDED = - ProvisioningManager.KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC; + ProvisioningManager.KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC; /** * diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index af5089f8e0d9..dcf339c1be2f 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -22,6 +22,7 @@ import android.content.IntentSender; import android.os.Bundle; import android.os.IBinder; import android.os.Messenger; +import android.os.ParcelFileDescriptor; import android.os.ResultReceiver; import android.os.WorkSource; import android.net.NetworkStats; @@ -2121,9 +2122,14 @@ interface ITelephony { void notifyOtaEmergencyNumberDbInstalled(); /** - * Override the file partition name for testing OTA emergency number database. + * Override a customized file partition name for OTA emergency number database. */ - void updateTestOtaEmergencyNumberDbFilePath(String otaFilePath); + void updateOtaEmergencyNumberDbFilePath(in ParcelFileDescriptor otaParcelFileDescriptor); + + /** + * Reset file partition to default for OTA emergency number database. + */ + void resetOtaEmergencyNumberDbFilePath(); /** * Enable or disable a logical modem stack associated with the slotIndex. diff --git a/test-mock/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java index 8283019a10ec..8f4bcccb0cba 100644 --- a/test-mock/src/android/test/mock/MockContentResolver.java +++ b/test-mock/src/android/test/mock/MockContentResolver.java @@ -25,6 +25,7 @@ import android.content.IContentProvider; import android.database.ContentObserver; import android.net.Uri; +import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -172,7 +173,7 @@ public class MockContentResolver extends ContentResolver { * from observers elsewhere in the system. */ @Override - public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer, + public void notifyChange(@NonNull Collection<Uri> uris, @Nullable ContentObserver observer, @NotifyFlags int flags) { } } diff --git a/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java new file mode 100644 index 000000000000..2fbfeba47b13 --- /dev/null +++ b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java @@ -0,0 +1,61 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteCallback; +import android.service.watchdog.ExplicitHealthCheckService; +import android.service.watchdog.IExplicitHealthCheckService; + +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; + +public class ExplicitHealthCheckServiceTest { + + private ExplicitHealthCheckService mExplicitHealthCheckService; + private static final String PACKAGE_NAME = "com.test.package"; + + @Before + public void setup() throws Exception { + mExplicitHealthCheckService = spy(ExplicitHealthCheckService.class); + } + + /** + * Test to verify that the correct information is sent in the callback when a package has + * passed an explicit health check. + */ + @Test + public void testNotifyHealthCheckPassed() throws Exception { + IBinder binder = mExplicitHealthCheckService.onBind(new Intent()); + CountDownLatch countDownLatch = new CountDownLatch(1); + RemoteCallback callback = new RemoteCallback(result -> { + assertThat(result.get(ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE)) + .isEqualTo(PACKAGE_NAME); + countDownLatch.countDown(); + }); + IExplicitHealthCheckService.Stub.asInterface(binder).setCallback(callback); + mExplicitHealthCheckService.notifyHealthCheckPassed(PACKAGE_NAME); + countDownLatch.await(); + } +} diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 8cc8cf4d2a97..819fc020e397 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -1063,6 +1063,52 @@ public class PackageWatchdogTest { assertThat(bootObserver2.mitigatedBootLoop()).isFalse(); } + /** + * Test to verify that Package Watchdog syncs health check requests with the controller + * correctly, and that the requests are only synced when the set of observed packages + * changes. + */ + @Test + public void testSyncHealthCheckRequests() { + TestController testController = spy(TestController.class); + testController.setSupportedPackages(List.of(APP_A, APP_B, APP_C)); + PackageWatchdog watchdog = createWatchdog(testController, true); + + TestObserver testObserver1 = new TestObserver(OBSERVER_NAME_1); + watchdog.registerHealthObserver(testObserver1); + watchdog.startObservingHealth(testObserver1, List.of(APP_A), LONG_DURATION); + mTestLooper.dispatchAll(); + + TestObserver testObserver2 = new TestObserver(OBSERVER_NAME_2); + watchdog.registerHealthObserver(testObserver2); + watchdog.startObservingHealth(testObserver2, List.of(APP_B), LONG_DURATION); + mTestLooper.dispatchAll(); + + TestObserver testObserver3 = new TestObserver(OBSERVER_NAME_3); + watchdog.registerHealthObserver(testObserver3); + watchdog.startObservingHealth(testObserver3, List.of(APP_C), LONG_DURATION); + mTestLooper.dispatchAll(); + + watchdog.unregisterHealthObserver(testObserver1); + mTestLooper.dispatchAll(); + + watchdog.unregisterHealthObserver(testObserver2); + mTestLooper.dispatchAll(); + + watchdog.unregisterHealthObserver(testObserver3); + mTestLooper.dispatchAll(); + + List<Set> expectedSyncRequests = List.of( + Set.of(APP_A), + Set.of(APP_A, APP_B), + Set.of(APP_A, APP_B, APP_C), + Set.of(APP_B, APP_C), + Set.of(APP_C), + Set.of() + ); + assertThat(testController.getSyncRequests()).isEqualTo(expectedSyncRequests); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() @@ -1219,6 +1265,7 @@ public class PackageWatchdogTest { private Consumer<String> mPassedConsumer; private Consumer<List<PackageConfig>> mSupportedConsumer; private Runnable mNotifySyncRunnable; + private List<Set> mSyncRequests = new ArrayList<>(); @Override public void setEnabled(boolean enabled) { @@ -1238,6 +1285,7 @@ public class PackageWatchdogTest { @Override public void syncRequests(Set<String> packages) { + mSyncRequests.add(packages); mRequestedPackages.clear(); if (mIsEnabled) { packages.retainAll(mSupportedPackages); @@ -1268,6 +1316,10 @@ public class PackageWatchdogTest { return Collections.emptyList(); } } + + public List<Set> getSyncRequests() { + return mSyncRequests; + } } private static class TestClock implements PackageWatchdog.SystemClock { diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 70be83f216da..a616c61b34f8 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -127,18 +127,12 @@ public class StagedRollbackTest { /** * Test rollbacks of staged installs involving only apks with bad update. - * Trigger rollback phase. This is expected to fail due to watchdog - * rebooting the test out from under it. + * Trigger rollback phase. */ @Test public void testBadApkOnly_Phase3() throws Exception { // One more crash to trigger rollback RollbackUtils.sendCrashBroadcast(TestApp.A, 1); - - // We expect the device to be rebooted automatically. Wait for that to happen. - // This device method will fail and the host will catch the assertion. - // If reboot doesn't happen, the host will fail the assertion. - Thread.sleep(TimeUnit.SECONDS.toMillis(120)); } /** diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 4afebb58c105..282f012dbf6f 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -22,7 +22,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; -import static org.testng.Assert.assertThrows; import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -136,7 +135,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { getDevice().reboot(); runPhase("testBadApkOnly_Phase2"); - assertThrows(AssertionError.class, () -> runPhase("testBadApkOnly_Phase3")); + // Trigger rollback and wait for reboot to happen + runPhase("testBadApkOnly_Phase3"); + assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(2))); + getDevice().waitForDeviceAvailable(); runPhase("testBadApkOnly_Phase4"); diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java index 8f7bebb8a9e4..6eb4587c8b55 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java @@ -139,7 +139,7 @@ public class TaskOrganizerMultiWindowTest extends Activity { mTaskView2.reparentTask(ti.token); } } - public void taskVanished(IWindowContainer wc) { + public void taskVanished(ActivityManager.RunningTaskInfo ti) { } public void transactionReady(int id, SurfaceControl.Transaction t) { mergedTransaction.merge(t); diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java index bd17751f183e..ade5c2e21fd9 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java @@ -49,7 +49,7 @@ public class TaskOrganizerPipTest extends Service { } catch (Exception e) { } } - public void taskVanished(IWindowContainer wc) { + public void taskVanished(ActivityManager.RunningTaskInfo ti) { } public void transactionReady(int id, SurfaceControl.Transaction t) { } 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 8e6f1985a7d9..e41517085c36 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java @@ -16,11 +16,15 @@ package com.google.android.test.windowinsetstests; +import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static java.lang.Math.max; import static java.lang.Math.min; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -37,6 +41,8 @@ import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Callback; import android.view.WindowInsetsAnimationControlListener; import android.view.WindowInsetsAnimationController; +import android.view.WindowInsetsController; +import android.view.WindowInsetsController.OnControllableInsetsChangedListener; import android.view.animation.LinearInterpolator; import android.widget.LinearLayout; @@ -82,8 +88,8 @@ public class WindowInsetsActivity extends AppCompatActivity { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mDown = event.getY(); - mDownInsets = v.getRootWindowInsets().getInsets(Type.ime()); - mShownAtDown = v.getRootWindowInsets().isVisible(Type.ime()); + mDownInsets = v.getRootWindowInsets().getInsets(ime()); + mShownAtDown = v.getRootWindowInsets().isVisible(ime()); mRequestedController = false; mCurrentRequest = null; break; @@ -94,7 +100,7 @@ public class WindowInsetsActivity extends AppCompatActivity { > mViewConfiguration.getScaledTouchSlop() && !mRequestedController) { mRequestedController = true; - v.getWindowInsetsController().controlWindowInsetsAnimation(Type.ime(), + v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), 1000, new LinearInterpolator(), mCurrentRequest = new WindowInsetsAnimationControlListener() { @Override @@ -189,6 +195,51 @@ public class WindowInsetsActivity extends AppCompatActivity { getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false)); } + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + getWindow().getInsetsController().addOnControllableInsetsChangedListener( + new OnControllableInsetsChangedListener() { + + boolean hasControl = false; + @Override + public void onControllableInsetsChanged(WindowInsetsController controller, + int types) { + if ((types & ime()) != 0 && !hasControl) { + hasControl = true; + controller.controlWindowInsetsAnimation(ime(), -1, + new LinearInterpolator(), + 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() { + } + }); + } + } + }); + } + static class Transition { private int mEndBottom; private int mStartBottom; @@ -200,7 +251,7 @@ public class WindowInsetsActivity extends AppCompatActivity { } void onPrepare(WindowInsetsAnimation animation) { - if ((animation.getTypeMask() & Type.ime()) != 0) { + if ((animation.getTypeMask() & ime()) != 0) { mInsetsAnimation = animation; } mStartBottom = mView.getBottom(); diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java index ca4ba63142a2..7a60cc105a26 100644 --- a/tests/net/common/java/android/net/CaptivePortalTest.java +++ b/tests/net/common/java/android/net/CaptivePortalTest.java @@ -18,19 +18,26 @@ package android.net; import static org.junit.Assert.assertEquals; +import android.os.Build; import android.os.RemoteException; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest public class CaptivePortalTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); + private static final int DEFAULT_TIMEOUT_MS = 5000; private static final String TEST_PACKAGE_NAME = "com.google.android.test"; @@ -84,6 +91,7 @@ public class CaptivePortalTest { assertEquals(result.mCode, CaptivePortal.APP_RETURN_WANTED_AS_IS); } + @IgnoreUpTo(Build.VERSION_CODES.Q) @Test public void testReevaluateNetwork() { final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.reevaluateNetwork()); diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java index 06c6301d66f3..99dac1439b34 100644 --- a/tests/net/common/java/android/net/LinkAddressTest.java +++ b/tests/net/common/java/android/net/LinkAddressTest.java @@ -28,8 +28,8 @@ import static android.system.OsConstants.RT_SCOPE_SITE; import static android.system.OsConstants.RT_SCOPE_UNIVERSE; import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; +import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; import static org.junit.Assert.assertEquals; @@ -38,11 +38,17 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.os.Build; import android.os.SystemClock; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -57,6 +63,8 @@ import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest public class LinkAddressTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); private static final String V4 = "192.0.2.1"; private static final String V6 = "2001:db8::1"; @@ -318,15 +326,29 @@ public class LinkAddressTest { l = new LinkAddress(V6_ADDRESS, 64, 123, 456); assertParcelingIsLossless(l); - l = new LinkAddress(V6_ADDRESS, 64, 123, 456, - 1L, 3600000L); - assertParcelingIsLossless(l); l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK); - assertParcelSane(l, 6); + assertParcelingIsLossless(l); } - @Test + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testLifetimeParceling() { + final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, 456, 1L, 3600000L); + assertParcelingIsLossless(l); + } + + @Test @IgnoreAfter(Build.VERSION_CODES.Q) + public void testFieldCount_Q() { + assertFieldCountEquals(4, LinkAddress.class); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testFieldCount() { + // Make sure any new field is covered by the above parceling tests when changing this number + assertFieldCountEquals(6, LinkAddress.class); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testDeprecationTime() { try { new LinkAddress(V6_ADDRESS, 64, 0, 456, @@ -347,7 +369,7 @@ public class LinkAddressTest { } catch (IllegalArgumentException expected) { } } - @Test + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testExpirationTime() { try { new LinkAddress(V6_ADDRESS, 64, 0, 456, @@ -366,10 +388,13 @@ public class LinkAddressTest { public void testGetFlags() { LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, RT_SCOPE_HOST); assertEquals(123, l.getFlags()); + } + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testGetFlags_Deprecation() { // Test if deprecated bit was added/remove automatically based on the provided deprecation // time - l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST, + LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST, 1L, LinkAddress.LIFETIME_PERMANENT); // Check if the flag is added automatically. assertTrue((l.getFlags() & IFA_F_DEPRECATED) != 0); @@ -458,8 +483,11 @@ public class LinkAddressTest { (IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC), RT_SCOPE_UNIVERSE); assertGlobalPreferred(l, "v6,global,tempaddr+optimistic"); + } - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testIsGlobalPreferred_DeprecatedInFuture() { + final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_UNIVERSE, SystemClock.elapsedRealtime() + 100000, SystemClock.elapsedRealtime() + 200000); // Although the deprecated bit is set, but the deprecation time is in the future, test diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt index d250ad3a2b12..173dbd1271f8 100644 --- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt +++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt @@ -16,16 +16,23 @@ package android.net +import android.os.Build import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.assertParcelSane +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @SmallTest class NetworkAgentConfigTest { - @Test + @Rule @JvmField + val ignoreRule = DevSdkIgnoreRule() + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) fun testParcelNetworkAgentConfig() { val config = NetworkAgentConfig.Builder().apply { setExplicitlySelected(true) diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java index fe51b3af4d72..1658262c17f6 100644 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ b/tests/net/common/java/android/net/RouteInfoTest.java @@ -19,19 +19,40 @@ package android.net; import static android.net.RouteInfo.RTN_UNREACHABLE; import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; +import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtilsKt.assertParcelSane; import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; -import android.test.suitebuilder.annotation.SmallTest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -import junit.framework.TestCase; +import android.os.Build; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; -public class RouteInfoTest extends TestCase { +@RunWith(AndroidJUnit4.class) +@SmallTest +public class RouteInfoTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); + + private static final int INVALID_ROUTE_TYPE = -1; private InetAddress Address(String addr) { return InetAddress.parseNumericAddress(addr); @@ -41,15 +62,32 @@ public class RouteInfoTest extends TestCase { return new IpPrefix(prefix); } - @SmallTest + @Test public void testConstructor() { RouteInfo r; - // Invalid input. try { r = new RouteInfo((IpPrefix) null, null, "rmnet0"); fail("Expected RuntimeException: destination and gateway null"); - } catch(RuntimeException e) {} + } catch (RuntimeException e) { } + + try { + r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "rmnet0", + INVALID_ROUTE_TYPE); + fail("Invalid route type should cause exception"); + } catch (IllegalArgumentException e) { } + + try { + r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("192.0.2.1"), "rmnet0", + RTN_UNREACHABLE); + fail("Address family mismatch should cause exception"); + } catch (IllegalArgumentException e) { } + + try { + r = new RouteInfo(Prefix("0.0.0.0/0"), Address("2001:db8::1"), "rmnet0", + RTN_UNREACHABLE); + fail("Address family mismatch should cause exception"); + } catch (IllegalArgumentException e) { } // Null destination is default route. r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null); @@ -74,6 +112,7 @@ public class RouteInfoTest extends TestCase { assertNull(r.getInterface()); } + @Test public void testMatches() { class PatchedRouteInfo { private final RouteInfo mRouteInfo; @@ -113,6 +152,7 @@ public class RouteInfoTest extends TestCase { assertFalse(ipv4Default.matches(Address("2001:db8::f00"))); } + @Test public void testEquals() { // IPv4 RouteInfo r1 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0"); @@ -146,6 +186,7 @@ public class RouteInfoTest extends TestCase { assertNotEqualEitherWay(r1, r3); } + @Test public void testHostAndDefaultRoutes() { RouteInfo r; @@ -228,6 +269,7 @@ public class RouteInfoTest extends TestCase { assertFalse(r.isIPv6Default()); } + @Test public void testTruncation() { LinkAddress l; RouteInfo r; @@ -244,6 +286,7 @@ public class RouteInfoTest extends TestCase { // Make sure that creating routes to multicast addresses doesn't throw an exception. Even though // there's nothing we can do with them, we don't want to crash if, e.g., someone calls // requestRouteToHostAddress("230.0.0.0", MOBILE_HIPRI); + @Test public void testMulticastRoute() { RouteInfo r; r = new RouteInfo(Prefix("230.0.0.0/32"), Address("192.0.2.1"), "wlan0"); @@ -251,16 +294,36 @@ public class RouteInfoTest extends TestCase { // No exceptions? Good. } + @Test public void testParceling() { RouteInfo r; + r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), null); + assertParcelingIsLossless(r); + r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); + assertParcelingIsLossless(r); + r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", RTN_UNREACHABLE); + assertParcelingIsLossless(r); + } - r = new RouteInfo(Prefix("::/0"), Address("2001:db8::"), null); + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testMtuParceling() { + final RouteInfo r = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::"), "testiface", + RTN_UNREACHABLE, 1450 /* mtu */); assertParcelingIsLossless(r); + } - r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); - assertParcelSane(r, 7); + @Test @IgnoreAfter(Build.VERSION_CODES.Q) + public void testFieldCount_Q() { + assertFieldCountEquals(6, RouteInfo.class); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testFieldCount() { + // Make sure any new field is covered by the above parceling tests when changing this number + assertFieldCountEquals(7, RouteInfo.class); } + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testMtu() { RouteInfo r; r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0", diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index c2c3ba3e8b1b..8c0c36b89bf9 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -2751,9 +2751,6 @@ public class ConnectivityServiceTest { // Expect NET_CAPABILITY_VALIDATED onAvailable callback. validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - // Expect no notification to be shown when captive portal disappears by itself - verify(mNotificationManager, never()).notifyAsUser( - anyString(), eq(NotificationType.LOGGED_IN.eventId), any(), any()); // Break network connectivity. // Expect NET_CAPABILITY_VALIDATED onLost callback. @@ -2815,8 +2812,6 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - verify(mNotificationManager, times(1)).notifyAsUser(anyString(), - eq(NotificationType.LOGGED_IN.eventId), any(), eq(UserHandle.ALL)); mCm.unregisterNetworkCallback(validatedCallback); mCm.unregisterNetworkCallback(captivePortalCallback); diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java index d57f2250fc5c..47db5d431671 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java @@ -238,20 +238,6 @@ public class NetworkNotificationManagerTest { } @Test - public void testSameLevelNotifications() { - final int id = 101; - final String tag = NetworkNotificationManager.tagFor(id); - - mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any()); - - mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any()); - } - - @Test public void testClearNotificationByType() { final int id = 101; final String tag = NetworkNotificationManager.tagFor(id); @@ -259,31 +245,25 @@ public class NetworkNotificationManagerTest { // clearNotification(int id, NotificationType notifyType) will check if given type is equal // to previous type or not. If they are equal then clear the notification; if they are not // equal then return. - - mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false); + mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false); verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any()); + .notifyAsUser(eq(tag), eq(NO_INTERNET.eventId), any(), any()); - // Previous notification is LOGGED_IN and given type is LOGGED_IN too. The notification + // Previous notification is NO_INTERNET and given type is NO_INTERNET too. The notification // should be cleared. - mManager.clearNotification(id, LOGGED_IN); + mManager.clearNotification(id, NO_INTERNET); verify(mNotificationManager, times(1)) - .cancelAsUser(eq(tag), eq(LOGGED_IN.eventId), any()); - - mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(2)) - .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any()); + .cancelAsUser(eq(tag), eq(NO_INTERNET.eventId), any()); - // LOST_INTERNET notification popup after LOGGED_IN notification. - mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false); + // SIGN_IN is popped-up. + mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false); verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any()); + .notifyAsUser(eq(tag), eq(SIGN_IN.eventId), any(), any()); - // Previous notification is LOST_INTERNET and given type is LOGGED_IN. The notification - // shouldn't be cleared. - mManager.clearNotification(id, LOGGED_IN); - // LOST_INTERNET shouldn't be cleared. + // The notification type is not matching previous one, PARTIAL_CONNECTIVITY won't be + // cleared. + mManager.clearNotification(id, PARTIAL_CONNECTIVITY); verify(mNotificationManager, never()) - .cancelAsUser(eq(tag), eq(LOST_INTERNET.eventId), any()); + .cancelAsUser(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId), any()); } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index a9e0b9abba9c..36deca3e37b7 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -64,6 +64,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.app.AlarmManager; import android.app.usage.NetworkStatsManager; import android.content.Context; @@ -163,7 +164,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private @Mock IBinder mBinder; private @Mock AlarmManager mAlarmManager; private HandlerThread mHandlerThread; - private Handler mHandler; private NetworkStatsService mService; private INetworkStatsSession mSession; @@ -192,15 +192,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); - mService = new NetworkStatsService( - mServiceContext, mNetManager, mAlarmManager, wakeLock, mClock, - mServiceContext.getSystemService(TelephonyManager.class), mSettings, - mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir)); mHandlerThread = new HandlerThread("HandlerThread"); - mHandlerThread.start(); - Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService); - mHandler = new Handler(mHandlerThread.getLooper(), callback); - mService.setHandler(mHandler, callback); + final NetworkStatsService.Dependencies deps = makeDependencies(); + mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock, + mClock, mServiceContext.getSystemService(TelephonyManager.class), mSettings, + mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir), deps); mElapsedRealtime = 0L; @@ -217,11 +213,21 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // catch INetworkManagementEventObserver during systemReady() ArgumentCaptor<INetworkManagementEventObserver> networkObserver = - ArgumentCaptor.forClass(INetworkManagementEventObserver.class); + ArgumentCaptor.forClass(INetworkManagementEventObserver.class); verify(mNetManager).registerObserver(networkObserver.capture()); mNetworkObserver = networkObserver.getValue(); } + @NonNull + private NetworkStatsService.Dependencies makeDependencies() { + return new NetworkStatsService.Dependencies() { + @Override + public HandlerThread makeHandlerThread() { + return mHandlerThread; + } + }; + } + @After public void tearDown() throws Exception { IoUtils.deleteContents(mStatsDir); @@ -234,6 +240,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mSession.close(); mService = null; + + mHandlerThread.quitSafely(); } @Test @@ -939,9 +947,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB assertEquals(minThresholdInBytes, request.thresholdInBytes); - // Send dummy message to make sure that any previous message has been handled - mHandler.sendMessage(mHandler.obtainMessage(-1)); - HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); + HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Make sure that the caller binder gets connected verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); @@ -1077,7 +1083,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // Simulates alert quota of the provider has been reached. cb.onAlertReached(); - HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); + HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Verifies that polling is triggered by alert reached. provider.expectStatsUpdate(0 /* unused */); @@ -1294,9 +1300,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private void forcePollAndWaitForIdle() { mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); - // Send dummy message to make sure that any previous message has been handled - mHandler.sendMessage(mHandler.obtainMessage(-1)); - HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); + HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); } static class LatchedHandler extends Handler { diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index cbf6fe88e565..b1e2487c54fe 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -120,12 +120,6 @@ cc_library { "liblog", "libcutils", ], - apex_available: [ - "//apex_available:platform", - //TODO(b/149781190): Remove this once statsd no longer depends on libstatslog - "com.android.os.statsd", - "test_com.android.os.statsd", - ], target: { android: { shared_libs: ["libstatssocket"], diff --git a/wifi/Android.bp b/wifi/Android.bp index 91174d3c3be2..f4d28817a7f2 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -177,14 +177,14 @@ droidstubs { java_library { name: "framework-wifi-stubs-publicapi", srcs: [":framework-wifi-stubs-srcs-publicapi"], - sdk_version: "module_current", + sdk_version: "current", installable: false, } java_library { name: "framework-wifi-stubs-systemapi", srcs: [":framework-wifi-stubs-srcs-systemapi"], - sdk_version: "module_current", + sdk_version: "system_current", libs: ["framework-annotations-lib"], installable: false, } diff --git a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl b/wifi/java/android/net/wifi/IScoreUpdateObserver.aidl index d691f41b2858..775fed7d47ef 100644 --- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl +++ b/wifi/java/android/net/wifi/IScoreUpdateObserver.aidl @@ -21,9 +21,9 @@ package android.net.wifi; * * @hide */ -oneway interface IScoreChangeCallback +oneway interface IScoreUpdateObserver { - void onScoreChange(int sessionId, int score); + void notifyScoreUpdate(int sessionId, int score); - void onTriggerUpdateOfWifiUsabilityStats(int sessionId); + void triggerUpdateOfWifiUsabilityStats(int sessionId); } diff --git a/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl index d9a3b0109a09..f96d037cbfea 100644 --- a/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl +++ b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl @@ -16,7 +16,7 @@ package android.net.wifi; -import android.net.wifi.IScoreChangeCallback; +import android.net.wifi.IScoreUpdateObserver; /** * Interface for Wi-Fi connected network scorer. @@ -25,9 +25,9 @@ import android.net.wifi.IScoreChangeCallback; */ oneway interface IWifiConnectedNetworkScorer { - void start(int sessionId); + void onStart(int sessionId); - void stop(int sessionId); + void onStop(int sessionId); - void setScoreChangeCallback(IScoreChangeCallback cbImpl); + void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl); } diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 9256c57ab4b9..70542b5d3c65 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -16,16 +16,15 @@ package android.net.wifi; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.net.wifi.WifiAnnotations.ChannelWidth; +import android.net.wifi.WifiAnnotations.WifiStandard; import android.os.Parcel; import android.os.Parcelable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; @@ -313,17 +312,6 @@ public class ScanResult implements Parcelable { */ public static final int WIFI_STANDARD_11AX = 6; - /** @hide */ - @IntDef(prefix = { "WIFI_STANDARD_" }, value = { - WIFI_STANDARD_UNKNOWN, - WIFI_STANDARD_LEGACY, - WIFI_STANDARD_11N, - WIFI_STANDARD_11AC, - WIFI_STANDARD_11AX - }) - @Retention(RetentionPolicy.SOURCE) - public @interface WifiStandard{} - /** * AP wifi standard. */ @@ -368,7 +356,7 @@ public class ScanResult implements Parcelable { * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ} * or {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ}. */ - public int channelWidth; + public @ChannelWidth int channelWidth; /** * Not used if the AP bandwidth is 20 MHz diff --git a/wifi/java/android/net/wifi/WifiAnnotations.java b/wifi/java/android/net/wifi/WifiAnnotations.java index 05e5b1d45684..acda7e06c95d 100644 --- a/wifi/java/android/net/wifi/WifiAnnotations.java +++ b/wifi/java/android/net/wifi/WifiAnnotations.java @@ -61,6 +61,26 @@ public final class WifiAnnotations { @Retention(RetentionPolicy.SOURCE) public @interface Bandwidth {} + @IntDef(prefix = { "CHANNEL_WIDTH_" }, value = { + ScanResult.CHANNEL_WIDTH_20MHZ, + ScanResult.CHANNEL_WIDTH_40MHZ, + ScanResult.CHANNEL_WIDTH_80MHZ, + ScanResult.CHANNEL_WIDTH_160MHZ, + ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ChannelWidth{} + + @IntDef(prefix = { "WIFI_STANDARD_" }, value = { + ScanResult.WIFI_STANDARD_UNKNOWN, + ScanResult.WIFI_STANDARD_LEGACY, + ScanResult.WIFI_STANDARD_11N, + ScanResult.WIFI_STANDARD_11AC, + ScanResult.WIFI_STANDARD_11AX, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WifiStandard{} + @IntDef(prefix = { "PROTOCOL_" }, value = { ScanResult.PROTOCOL_NONE, ScanResult.PROTOCOL_WPA, diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 5a7bf4b15f1a..ba68d170364c 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -498,11 +498,15 @@ public class WifiConfiguration implements Parcelable { allowedProtocols.set(WifiConfiguration.Protocol.RSN); allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256); allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); requirePmf = true; break; case SECURITY_TYPE_EAP_SUITE_B: allowedProtocols.set(WifiConfiguration.Protocol.RSN); + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192); allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256); allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); @@ -515,7 +519,9 @@ public class WifiConfiguration implements Parcelable { allowedProtocols.set(WifiConfiguration.Protocol.RSN); allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE); allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256); allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); requirePmf = true; break; case SECURITY_TYPE_WAPI_PSK: @@ -629,13 +635,6 @@ public class WifiConfiguration implements Parcelable { public String preSharedKey; /** - * Optional SAE Password Id for use with WPA3-SAE. It is an ASCII string. - * @hide - */ - @SystemApi - public @Nullable String saePasswordId; - - /** * Four WEP keys. For each of the four values, provide either an ASCII * string enclosed in double quotation marks (e.g., {@code "abcdef"}), * a string of hex digits (e.g., {@code 0102030405}), or an empty string @@ -2328,9 +2327,6 @@ public class WifiConfiguration implements Parcelable { sbuf.append('*'); } - sbuf.append('\n').append(" SAE Password Id: "); - sbuf.append(this.saePasswordId); - sbuf.append("\nEnterprise config:\n"); sbuf.append(enterpriseConfig); @@ -2725,7 +2721,6 @@ public class WifiConfiguration implements Parcelable { providerFriendlyName = source.providerFriendlyName; isHomeProviderNetwork = source.isHomeProviderNetwork; preSharedKey = source.preSharedKey; - saePasswordId = source.saePasswordId; mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus()); apBand = source.apBand; @@ -2813,7 +2808,6 @@ public class WifiConfiguration implements Parcelable { dest.writeLong(roamingConsortiumId); } dest.writeString(preSharedKey); - dest.writeString(saePasswordId); for (String wepKey : wepKeys) { dest.writeString(wepKey); } @@ -2889,7 +2883,6 @@ public class WifiConfiguration implements Parcelable { config.roamingConsortiumIds[i] = in.readLong(); } config.preSharedKey = in.readString(); - config.saePasswordId = in.readString(); for (int i = 0; i < config.wepKeys.length; i++) { config.wepKeys[i] = in.readString(); } diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 142854a9e41b..70c5e72e4e0c 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -103,7 +103,7 @@ public class WifiInfo implements Parcelable { /** * Wi-Fi standard for the connection */ - private @ScanResult.WifiStandard int mWifiStandard; + private @WifiAnnotations.WifiStandard int mWifiStandard; /** * The unit in which links speeds are expressed. @@ -518,7 +518,7 @@ public class WifiInfo implements Parcelable { * Sets the Wi-Fi standard * @hide */ - public void setWifiStandard(@ScanResult.WifiStandard int wifiStandard) { + public void setWifiStandard(@WifiAnnotations.WifiStandard int wifiStandard) { mWifiStandard = wifiStandard; } @@ -526,7 +526,7 @@ public class WifiInfo implements Parcelable { * Get connection Wi-Fi standard * @return the connection Wi-Fi standard */ - public @ScanResult.WifiStandard int getWifiStandard() { + public @WifiAnnotations.WifiStandard int getWifiStandard() { return mWifiStandard; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 9703fa61ea23..96beacd0a236 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -2563,7 +2563,7 @@ public class WifiManager { * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} * @return {@code true} if supported, {@code false} otherwise. */ - public boolean isWifiStandardSupported(@ScanResult.WifiStandard int standard) { + public boolean isWifiStandardSupported(@WifiAnnotations.WifiStandard int standard) { try { return mService.isWifiStandardSupported(standard); } catch (RemoteException e) { @@ -2735,27 +2735,30 @@ public class WifiManager { } /** - * Return the filtered ScanResults which may be authenticated by the suggested network - * configurations. - * @param networkSuggestions The list of {@link WifiNetworkSuggestion} - * @param scanResults The scan results to be filtered, this is optional, if it is null or - * empty, wifi system would use the recent scan results in the system. - * @return The map of {@link WifiNetworkSuggestion} and the list of {@link ScanResult} which - * may be authenticated by the corresponding network configuration. + * Get the filtered ScanResults which match the network configurations specified by the + * {@code networkSuggestionsToMatch}. Suggestions which use {@link WifiConfiguration} use + * SSID and the security type to match. Suggestions which use {@link PasspointConfigration} + * use the matching rules of Hotspot 2.0. + * @param networkSuggestionsToMatch The list of {@link WifiNetworkSuggestion} to match against. + * These may or may not be suggestions which are installed on the device. + * @param scanResults The scan results to be filtered. Optional - if not provided(empty list), + * the Wi-Fi service will use the most recent scan results which the system has. + * @return The map of {@link WifiNetworkSuggestion} to the list of {@link ScanResult} + * corresponding to networks which match them. * @hide */ @SystemApi @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) @NonNull public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults( - @NonNull List<WifiNetworkSuggestion> networkSuggestions, + @NonNull List<WifiNetworkSuggestion> networkSuggestionsToMatch, @Nullable List<ScanResult> scanResults) { - if (networkSuggestions == null) { + if (networkSuggestionsToMatch == null) { throw new IllegalArgumentException("networkSuggestions must not be null."); } try { return mService.getMatchingScanResults( - networkSuggestions, scanResults, + networkSuggestionsToMatch, scanResults, mContext.getOpPackageName(), mContext.getFeatureId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -2826,7 +2829,7 @@ public class WifiManager { */ @Nullable @SystemApi - @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode() { try { return mService.getCountryCode(); @@ -3372,7 +3375,7 @@ public class WifiManager { */ @NonNull @SystemApi - @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public SoftApConfiguration getSoftApConfiguration() { try { return mService.getSoftApConfiguration(); @@ -4989,7 +4992,7 @@ public class WifiManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset() { try { mService.factoryReset(mContext.getOpPackageName()); @@ -5986,22 +5989,22 @@ public class WifiManager { } /** - * Callback interface for framework to receive network status changes and trigger of updating + * Callback interface for framework to receive network status updates and trigger of updating * {@link WifiUsabilityStatsEntry}. * * @hide */ @SystemApi - public interface ScoreChangeCallback { + public interface ScoreUpdateObserver { /** * Called by applications to indicate network status. * * @param sessionId The ID to indicate current Wi-Fi network connection obtained from - * {@link WifiConnectedNetworkScorer#start(int)}. + * {@link WifiConnectedNetworkScorer#onStart(int)}. * @param score The score representing link quality of current Wi-Fi network connection. * Populated by connected network scorer in applications.. */ - void onScoreChange(int sessionId, int score); + void notifyScoreUpdate(int sessionId, int score); /** * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}. @@ -6009,36 +6012,36 @@ public class WifiManager { * {@link addOnWifiUsabilityStatsListener(Executor, OnWifiUsabilityStatsListener)}. * * @param sessionId The ID to indicate current Wi-Fi network connection obtained from - * {@link WifiConnectedNetworkScorer#start(int)}. + * {@link WifiConnectedNetworkScorer#onStart(int)}. */ - void onTriggerUpdateOfWifiUsabilityStats(int sessionId); + void triggerUpdateOfWifiUsabilityStats(int sessionId); } /** - * Callback proxy for {@link ScoreChangeCallback} objects. + * Callback proxy for {@link ScoreUpdateObserver} objects. * * @hide */ - private class ScoreChangeCallbackProxy implements ScoreChangeCallback { - private final IScoreChangeCallback mScoreChangeCallback; + private class ScoreUpdateObserverProxy implements ScoreUpdateObserver { + private final IScoreUpdateObserver mScoreUpdateObserver; - private ScoreChangeCallbackProxy(IScoreChangeCallback callback) { - mScoreChangeCallback = callback; + private ScoreUpdateObserverProxy(IScoreUpdateObserver observer) { + mScoreUpdateObserver = observer; } @Override - public void onScoreChange(int sessionId, int score) { + public void notifyScoreUpdate(int sessionId, int score) { try { - mScoreChangeCallback.onScoreChange(sessionId, score); + mScoreUpdateObserver.notifyScoreUpdate(sessionId, score); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override - public void onTriggerUpdateOfWifiUsabilityStats(int sessionId) { + public void triggerUpdateOfWifiUsabilityStats(int sessionId) { try { - mScoreChangeCallback.onTriggerUpdateOfWifiUsabilityStats(sessionId); + mScoreUpdateObserver.triggerUpdateOfWifiUsabilityStats(sessionId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -6058,21 +6061,21 @@ public class WifiManager { * Called by framework to indicate the start of a network connection. * @param sessionId The ID to indicate current Wi-Fi network connection. */ - void start(int sessionId); + void onStart(int sessionId); /** * Called by framework to indicate the end of a network connection. * @param sessionId The ID to indicate current Wi-Fi network connection obtained from - * {@link WifiConnectedNetworkScorer#start(int)}. + * {@link WifiConnectedNetworkScorer#onStart(int)}. */ - void stop(int sessionId); + void onStop(int sessionId); /** * Framework sets callback for score change events after application sets its scorer. - * @param cbImpl The instance for {@link WifiManager#ScoreChangeCallback}. Should be + * @param observerImpl The instance for {@link WifiManager#ScoreUpdateObserver}. Should be * implemented and instantiated by framework. */ - void setScoreChangeCallback(@NonNull ScoreChangeCallback cbImpl); + void onSetScoreUpdateObserver(@NonNull ScoreUpdateObserver observerImpl); } /** @@ -6090,32 +6093,32 @@ public class WifiManager { } @Override - public void start(int sessionId) { + public void onStart(int sessionId) { if (mVerboseLoggingEnabled) { - Log.v(TAG, "WifiConnectedNetworkScorer: " + "start: sessionId=" + sessionId); + Log.v(TAG, "WifiConnectedNetworkScorer: " + "onStart: sessionId=" + sessionId); } Binder.clearCallingIdentity(); - mExecutor.execute(() -> mScorer.start(sessionId)); + mExecutor.execute(() -> mScorer.onStart(sessionId)); } @Override - public void stop(int sessionId) { + public void onStop(int sessionId) { if (mVerboseLoggingEnabled) { - Log.v(TAG, "WifiConnectedNetworkScorer: " + "stop: sessionId=" + sessionId); + Log.v(TAG, "WifiConnectedNetworkScorer: " + "onStop: sessionId=" + sessionId); } Binder.clearCallingIdentity(); - mExecutor.execute(() -> mScorer.stop(sessionId)); + mExecutor.execute(() -> mScorer.onStop(sessionId)); } @Override - public void setScoreChangeCallback(IScoreChangeCallback cbImpl) { + public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) { if (mVerboseLoggingEnabled) { Log.v(TAG, "WifiConnectedNetworkScorer: " - + "setScoreChangeCallback: cbImpl=" + cbImpl); + + "onSetScoreUpdateObserver: observerImpl=" + observerImpl); } Binder.clearCallingIdentity(); - mExecutor.execute(() -> mScorer.setScoreChangeCallback( - new ScoreChangeCallbackProxy(cbImpl))); + mExecutor.execute(() -> mScorer.onSetScoreUpdateObserver( + new ScoreUpdateObserverProxy(observerImpl))); } } diff --git a/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java b/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java index a045aad9f64c..bb0cc975a3db 100644 --- a/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java +++ b/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java @@ -19,6 +19,8 @@ package android.net.wifi.nl80211; import android.annotation.NonNull; import android.annotation.SystemApi; import android.net.wifi.ScanResult; +import android.net.wifi.WifiAnnotations.ChannelWidth; +import android.net.wifi.WifiAnnotations.WifiStandard; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -28,6 +30,9 @@ import java.util.Objects; /** * DeviceWiphyCapabilities for wificond * + * Contains the WiFi physical layer attributes and capabilities of the device. + * It is used to collect these attributes from the device driver via wificond. + * * @hide */ @SystemApi @@ -61,7 +66,7 @@ public final class DeviceWiphyCapabilities implements Parcelable { * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} * @return {@code true} if supported, {@code false} otherwise. */ - public boolean isWifiStandardSupported(int standard) { + public boolean isWifiStandardSupported(@WifiStandard int standard) { switch (standard) { case ScanResult.WIFI_STANDARD_LEGACY: return true; @@ -84,7 +89,7 @@ public final class DeviceWiphyCapabilities implements Parcelable { * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} * @param support {@code true} if supported, {@code false} otherwise. */ - public void setWifiStandardSupport(int standard, boolean support) { + public void setWifiStandardSupport(@WifiStandard int standard, boolean support) { switch (standard) { case ScanResult.WIFI_STANDARD_11N: m80211nSupported = support; @@ -107,7 +112,7 @@ public final class DeviceWiphyCapabilities implements Parcelable { * * @return {@code true} if supported, {@code false} otherwise. */ - public boolean isChannelWidthSupported(int chWidth) { + public boolean isChannelWidthSupported(@ChannelWidth int chWidth) { switch (chWidth) { case ScanResult.CHANNEL_WIDTH_20MHZ: return true; @@ -131,8 +136,10 @@ public final class DeviceWiphyCapabilities implements Parcelable { * @param chWidth valid values are {@link ScanResult#CHANNEL_WIDTH_160MHZ} and * {@link ScanResult#CHANNEL_WIDTH_80MHZ_PLUS_MHZ} * @param support {@code true} if supported, {@code false} otherwise. + * + * @hide */ - public void setChannelWidthSupported(int chWidth, boolean support) { + public void setChannelWidthSupported(@ChannelWidth int chWidth, boolean support) { switch (chWidth) { case ScanResult.CHANNEL_WIDTH_160MHZ: mChannelWidth160MhzSupported = support; @@ -159,6 +166,8 @@ public final class DeviceWiphyCapabilities implements Parcelable { * Set maximum number of transmit spatial streams * * @param streams number of spatial streams + * + * @hide */ public void setMaxNumberTxSpatialStreams(int streams) { mMaxNumberTxSpatialStreams = streams; @@ -177,6 +186,8 @@ public final class DeviceWiphyCapabilities implements Parcelable { * Set maximum number of receive spatial streams * * @param streams number of streams + * + * @hide */ public void setMaxNumberRxSpatialStreams(int streams) { mMaxNumberRxSpatialStreams = streams; diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index 9c2cad99614d..a310ff6404e7 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -1805,9 +1805,7 @@ public class WifiP2pManager { * @hide */ @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.CONNECTIVITY_INTERNAL, - android.Manifest.permission.CONFIGURE_WIFI_DISPLAY}) + @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setMiracastMode(@MiracastMode int mode) { try { mService.setMiracastMode(mode); diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml index 987fee79fefd..34e2e3af9cda 100644 --- a/wifi/tests/AndroidTest.xml +++ b/wifi/tests/AndroidTest.xml @@ -25,4 +25,10 @@ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> </test> + + <!-- Only run FrameworksWifiApiTests in MTS if the Wifi Mainline module is installed. --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.wifi" /> + </object> </configuration> diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java index b5c74d1d01b1..4c22d5d6dc7e 100644 --- a/wifi/tests/src/android/net/wifi/ScanResultTest.java +++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java @@ -42,7 +42,7 @@ public class ScanResultTest { public static final int TEST_LEVEL = -56; public static final int TEST_FREQUENCY = 2412; public static final long TEST_TSF = 04660l; - public static final @ScanResult.WifiStandard int TEST_WIFI_STANDARD = + public static final @WifiAnnotations.WifiStandard int TEST_WIFI_STANDARD = ScanResult.WIFI_STANDARD_11AC; /** diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index 047a64b25733..e210e4fec98e 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -423,7 +423,9 @@ public class WifiConfigurationTest { assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)); assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256)); assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256)); assertTrue(config.requirePmf); } @@ -440,7 +442,9 @@ public class WifiConfigurationTest { assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)); assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP)); + assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256)); assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP)); + assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256)); assertTrue(config.requirePmf); } @@ -456,6 +460,8 @@ public class WifiConfigurationTest { config.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B); assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)); + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)); + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)); assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256)); assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256)); assertTrue(config.allowedGroupManagementCiphers diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 76ac8373374a..90d6241aaa55 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -2349,23 +2349,24 @@ public class WifiManagerTest { } /** - * Verify that Wi-Fi connected scorer receives score change callback after registeration. + * Verify that Wi-Fi connected scorer receives score update observer after registeration. */ @Test - public void verifyScorerReceiveScoreChangeCallbackAfterRegistration() throws Exception { + public void verifyScorerReceiveScoreUpdateObserverAfterRegistration() throws Exception { mExecutor = new SynchronousExecutor(); mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> scorerCaptor = ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class); verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), scorerCaptor.capture()); - scorerCaptor.getValue().setScoreChangeCallback(any()); + scorerCaptor.getValue().onSetScoreUpdateObserver(any()); mLooper.dispatchAll(); - verify(mWifiConnectedNetworkScorer).setScoreChangeCallback(any()); + verify(mWifiConnectedNetworkScorer).onSetScoreUpdateObserver(any()); } /** - * Verify that Wi-Fi connected scorer receives session ID when start/stop methods are called. + * Verify that Wi-Fi connected scorer receives session ID when onStart/onStop methods + * are called. */ @Test public void verifyScorerReceiveSessionIdWhenStartStopIsCalled() throws Exception { @@ -2375,11 +2376,11 @@ public class WifiManagerTest { ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class); verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), callbackCaptor.capture()); - callbackCaptor.getValue().start(0); - callbackCaptor.getValue().stop(10); + callbackCaptor.getValue().onStart(0); + callbackCaptor.getValue().onStop(10); mLooper.dispatchAll(); - verify(mWifiConnectedNetworkScorer).start(0); - verify(mWifiConnectedNetworkScorer).stop(10); + verify(mWifiConnectedNetworkScorer).onStart(0); + verify(mWifiConnectedNetworkScorer).onStop(10); } @Test |